implement SCRAM-SHA512
This commit is contained in:
parent
2a57c92f63
commit
0e54d8a2cf
|
@ -7,22 +7,24 @@ import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
public class Anonymous extends SaslMechanism {
|
public class Anonymous extends SaslMechanism {
|
||||||
|
|
||||||
public Anonymous(TagWriter tagWriter, Account account, SecureRandom rng) {
|
public static final String MECHANISM = "ANONYMOUS";
|
||||||
super(tagWriter, account, rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public Anonymous(TagWriter tagWriter, Account account, SecureRandom rng) {
|
||||||
public int getPriority() {
|
super(tagWriter, account, rng);
|
||||||
return 0;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMechanism() {
|
public int getPriority() {
|
||||||
return "ANONYMOUS";
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getClientFirstMessage() {
|
public String getMechanism() {
|
||||||
return "";
|
return MECHANISM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientFirstMessage() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,79 +12,82 @@ import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.xml.TagWriter;
|
import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
public class DigestMd5 extends SaslMechanism {
|
public class DigestMd5 extends SaslMechanism {
|
||||||
public DigestMd5(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
|
||||||
super(tagWriter, account, rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public static final String MECHANISM = "DIGEST-MD5";
|
||||||
public int getPriority() {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public DigestMd5(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
||||||
public String getMechanism() {
|
super(tagWriter, account, rng);
|
||||||
return "DIGEST-MD5";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private State state = State.INITIAL;
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getResponse(final String challenge) throws AuthenticationException {
|
public String getMechanism() {
|
||||||
switch (state) {
|
return MECHANISM;
|
||||||
case INITIAL:
|
}
|
||||||
state = State.RESPONSE_SENT;
|
|
||||||
final String encodedResponse;
|
|
||||||
try {
|
|
||||||
final Tokenizer tokenizer = new Tokenizer(Base64.decode(challenge, Base64.DEFAULT));
|
|
||||||
String nonce = "";
|
|
||||||
for (final String token : tokenizer) {
|
|
||||||
final String[] parts = token.split("=", 2);
|
|
||||||
if (parts[0].equals("nonce")) {
|
|
||||||
nonce = parts[1].replace("\"", "");
|
|
||||||
} else if (parts[0].equals("rspauth")) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
final String digestUri = "xmpp/" + account.getServer();
|
|
||||||
final String nonceCount = "00000001";
|
|
||||||
final String x = account.getUsername() + ":" + account.getServer() + ":"
|
|
||||||
+ account.getPassword();
|
|
||||||
final MessageDigest md = MessageDigest.getInstance("MD5");
|
|
||||||
final byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
|
|
||||||
final String cNonce = CryptoHelper.random(100,rng);
|
|
||||||
final byte[] a1 = CryptoHelper.concatenateByteArrays(y,
|
|
||||||
(":" + nonce + ":" + cNonce).getBytes(Charset.defaultCharset()));
|
|
||||||
final String a2 = "AUTHENTICATE:" + digestUri;
|
|
||||||
final String ha1 = CryptoHelper.bytesToHex(md.digest(a1));
|
|
||||||
final String ha2 = CryptoHelper.bytesToHex(md.digest(a2.getBytes(Charset
|
|
||||||
.defaultCharset())));
|
|
||||||
final String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce
|
|
||||||
+ ":auth:" + ha2;
|
|
||||||
final String response = CryptoHelper.bytesToHex(md.digest(kd.getBytes(Charset
|
|
||||||
.defaultCharset())));
|
|
||||||
final String saslString = "username=\"" + account.getUsername()
|
|
||||||
+ "\",realm=\"" + account.getServer() + "\",nonce=\""
|
|
||||||
+ nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount
|
|
||||||
+ ",qop=auth,digest-uri=\"" + digestUri + "\",response="
|
|
||||||
+ response + ",charset=utf-8";
|
|
||||||
encodedResponse = Base64.encodeToString(
|
|
||||||
saslString.getBytes(Charset.defaultCharset()),
|
|
||||||
Base64.NO_WRAP);
|
|
||||||
} catch (final NoSuchAlgorithmException e) {
|
|
||||||
throw new AuthenticationException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return encodedResponse;
|
private State state = State.INITIAL;
|
||||||
case RESPONSE_SENT:
|
|
||||||
state = State.VALID_SERVER_RESPONSE;
|
@Override
|
||||||
break;
|
public String getResponse(final String challenge) throws AuthenticationException {
|
||||||
case VALID_SERVER_RESPONSE:
|
switch (state) {
|
||||||
if (challenge==null) {
|
case INITIAL:
|
||||||
return null; //everything is fine
|
state = State.RESPONSE_SENT;
|
||||||
}
|
final String encodedResponse;
|
||||||
default:
|
try {
|
||||||
throw new InvalidStateException(state);
|
final Tokenizer tokenizer = new Tokenizer(Base64.decode(challenge, Base64.DEFAULT));
|
||||||
}
|
String nonce = "";
|
||||||
return null;
|
for (final String token : tokenizer) {
|
||||||
}
|
final String[] parts = token.split("=", 2);
|
||||||
|
if (parts[0].equals("nonce")) {
|
||||||
|
nonce = parts[1].replace("\"", "");
|
||||||
|
} else if (parts[0].equals("rspauth")) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
final String digestUri = "xmpp/" + account.getServer();
|
||||||
|
final String nonceCount = "00000001";
|
||||||
|
final String x = account.getUsername() + ":" + account.getServer() + ":"
|
||||||
|
+ account.getPassword();
|
||||||
|
final MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
final byte[] y = md.digest(x.getBytes(Charset.defaultCharset()));
|
||||||
|
final String cNonce = CryptoHelper.random(100, rng);
|
||||||
|
final byte[] a1 = CryptoHelper.concatenateByteArrays(y,
|
||||||
|
(":" + nonce + ":" + cNonce).getBytes(Charset.defaultCharset()));
|
||||||
|
final String a2 = "AUTHENTICATE:" + digestUri;
|
||||||
|
final String ha1 = CryptoHelper.bytesToHex(md.digest(a1));
|
||||||
|
final String ha2 = CryptoHelper.bytesToHex(md.digest(a2.getBytes(Charset
|
||||||
|
.defaultCharset())));
|
||||||
|
final String kd = ha1 + ":" + nonce + ":" + nonceCount + ":" + cNonce
|
||||||
|
+ ":auth:" + ha2;
|
||||||
|
final String response = CryptoHelper.bytesToHex(md.digest(kd.getBytes(Charset
|
||||||
|
.defaultCharset())));
|
||||||
|
final String saslString = "username=\"" + account.getUsername()
|
||||||
|
+ "\",realm=\"" + account.getServer() + "\",nonce=\""
|
||||||
|
+ nonce + "\",cnonce=\"" + cNonce + "\",nc=" + nonceCount
|
||||||
|
+ ",qop=auth,digest-uri=\"" + digestUri + "\",response="
|
||||||
|
+ response + ",charset=utf-8";
|
||||||
|
encodedResponse = Base64.encodeToString(
|
||||||
|
saslString.getBytes(Charset.defaultCharset()),
|
||||||
|
Base64.NO_WRAP);
|
||||||
|
} catch (final NoSuchAlgorithmException e) {
|
||||||
|
throw new AuthenticationException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodedResponse;
|
||||||
|
case RESPONSE_SENT:
|
||||||
|
state = State.VALID_SERVER_RESPONSE;
|
||||||
|
break;
|
||||||
|
case VALID_SERVER_RESPONSE:
|
||||||
|
if (challenge == null) {
|
||||||
|
return null; //everything is fine
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new InvalidStateException(state);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package eu.siacs.conversations.crypto.sasl;
|
package eu.siacs.conversations.crypto.sasl;
|
||||||
|
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
@ -8,22 +9,24 @@ import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
public class External extends SaslMechanism {
|
public class External extends SaslMechanism {
|
||||||
|
|
||||||
public External(TagWriter tagWriter, Account account, SecureRandom rng) {
|
public static final String MECHANISM = "EXTERNAL";
|
||||||
super(tagWriter, account, rng);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public External(TagWriter tagWriter, Account account, SecureRandom rng) {
|
||||||
public int getPriority() {
|
super(tagWriter, account, rng);
|
||||||
return 25;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMechanism() {
|
public int getPriority() {
|
||||||
return "EXTERNAL";
|
return 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getClientFirstMessage() {
|
public String getMechanism() {
|
||||||
return Base64.encodeToString(account.getJid().asBareJid().toEscapedString().getBytes(),Base64.NO_WRAP);
|
return MECHANISM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientFirstMessage() {
|
||||||
|
return Base64.encodeToString(account.getJid().asBareJid().toEscapedString().getBytes(), Base64.NO_WRAP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,27 +8,30 @@ import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.xml.TagWriter;
|
import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
public class Plain extends SaslMechanism {
|
public class Plain extends SaslMechanism {
|
||||||
public Plain(final TagWriter tagWriter, final Account account) {
|
|
||||||
super(tagWriter, account, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public static final String MECHANISM = "PLAIN";
|
||||||
public int getPriority() {
|
|
||||||
return 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
public Plain(final TagWriter tagWriter, final Account account) {
|
||||||
public String getMechanism() {
|
super(tagWriter, account, null);
|
||||||
return "PLAIN";
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getClientFirstMessage() {
|
public int getPriority() {
|
||||||
return getMessage(account.getUsername(), account.getPassword());
|
return 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getMessage(String username, String password) {
|
@Override
|
||||||
final String message = '\u0000' + username + '\u0000' + password;
|
public String getMechanism() {
|
||||||
return Base64.encodeToString(message.getBytes(Charset.defaultCharset()), Base64.NO_WRAP);
|
return MECHANISM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClientFirstMessage() {
|
||||||
|
return getMessage(account.getUsername(), account.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getMessage(String username, String password) {
|
||||||
|
final String message = '\u0000' + username + '\u0000' + password;
|
||||||
|
return Base64.encodeToString(message.getBytes(Charset.defaultCharset()), Base64.NO_WRAP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,60 +7,63 @@ import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
public abstract class SaslMechanism {
|
public abstract class SaslMechanism {
|
||||||
|
|
||||||
final protected TagWriter tagWriter;
|
final protected TagWriter tagWriter;
|
||||||
final protected Account account;
|
final protected Account account;
|
||||||
final protected SecureRandom rng;
|
final protected SecureRandom rng;
|
||||||
|
|
||||||
protected enum State {
|
protected enum State {
|
||||||
INITIAL,
|
INITIAL,
|
||||||
AUTH_TEXT_SENT,
|
AUTH_TEXT_SENT,
|
||||||
RESPONSE_SENT,
|
RESPONSE_SENT,
|
||||||
VALID_SERVER_RESPONSE,
|
VALID_SERVER_RESPONSE,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class AuthenticationException extends Exception {
|
public static class AuthenticationException extends Exception {
|
||||||
public AuthenticationException(final String message) {
|
public AuthenticationException(final String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticationException(final Exception inner) {
|
public AuthenticationException(final Exception inner) {
|
||||||
super(inner);
|
super(inner);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthenticationException(final String message, final Exception exception) {
|
public AuthenticationException(final String message, final Exception exception) {
|
||||||
super(message,exception);
|
super(message, exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class InvalidStateException extends AuthenticationException {
|
public static class InvalidStateException extends AuthenticationException {
|
||||||
public InvalidStateException(final String message) {
|
public InvalidStateException(final String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public InvalidStateException(final State state) {
|
public InvalidStateException(final State state) {
|
||||||
this("Invalid state: " + state.toString());
|
this("Invalid state: " + state.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SaslMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
public SaslMechanism(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
||||||
this.tagWriter = tagWriter;
|
this.tagWriter = tagWriter;
|
||||||
this.account = account;
|
this.account = account;
|
||||||
this.rng = rng;
|
this.rng = rng;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The priority is used to pin the authentication mechanism. If authentication fails, it MAY be retried with another
|
* The priority is used to pin the authentication mechanism. If authentication fails, it MAY be retried with another
|
||||||
* mechanism of the same priority, but MUST NOT be tried with a mechanism of lower priority (to prevent downgrade
|
* mechanism of the same priority, but MUST NOT be tried with a mechanism of lower priority (to prevent downgrade
|
||||||
* attacks).
|
* attacks).
|
||||||
* @return An arbitrary int representing the priority
|
*
|
||||||
*/
|
* @return An arbitrary int representing the priority
|
||||||
public abstract int getPriority();
|
*/
|
||||||
|
public abstract int getPriority();
|
||||||
|
|
||||||
public abstract String getMechanism();
|
public abstract String getMechanism();
|
||||||
public String getClientFirstMessage() {
|
|
||||||
return "";
|
public String getClientFirstMessage() {
|
||||||
}
|
return "";
|
||||||
public String getResponse(final String challenge) throws AuthenticationException {
|
}
|
||||||
return "";
|
|
||||||
}
|
public String getResponse(final String challenge) throws AuthenticationException {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package eu.siacs.conversations.crypto.sasl;
|
package eu.siacs.conversations.crypto.sasl;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
import com.google.common.base.Objects;
|
||||||
|
@ -21,7 +19,6 @@ import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
import eu.siacs.conversations.utils.CryptoHelper;
|
||||||
import eu.siacs.conversations.xml.TagWriter;
|
import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
|
|
||||||
abstract class ScramMechanism extends SaslMechanism {
|
abstract class ScramMechanism extends SaslMechanism {
|
||||||
// TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage.
|
// TODO: When channel binding (SCRAM-SHA1-PLUS) is supported in future, generalize this to indicate support and/or usage.
|
||||||
private final static String GS2_HEADER = "n,,";
|
private final static String GS2_HEADER = "n,,";
|
||||||
|
|
|
@ -11,27 +11,29 @@ import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
public class ScramSha1 extends ScramMechanism {
|
public class ScramSha1 extends ScramMechanism {
|
||||||
|
|
||||||
@Override
|
public static final String MECHANISM = "SCRAM-SHA-1";
|
||||||
protected HMac getHMAC() {
|
|
||||||
return new HMac(new SHA1Digest());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Digest getDigest() {
|
protected HMac getHMAC() {
|
||||||
return new SHA1Digest();
|
return new HMac(new SHA1Digest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScramSha1(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
@Override
|
||||||
super(tagWriter, account, rng);
|
protected Digest getDigest() {
|
||||||
}
|
return new SHA1Digest();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public ScramSha1(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
||||||
public int getPriority() {
|
super(tagWriter, account, rng);
|
||||||
return 20;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMechanism() {
|
public int getPriority() {
|
||||||
return "SCRAM-SHA-1";
|
return 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMechanism() {
|
||||||
|
return MECHANISM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,27 +11,29 @@ import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
public class ScramSha256 extends ScramMechanism {
|
public class ScramSha256 extends ScramMechanism {
|
||||||
|
|
||||||
@Override
|
public static final String MECHANISM = "SCRAM-SHA-256";
|
||||||
protected HMac getHMAC() {
|
|
||||||
return new HMac(new SHA256Digest());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Digest getDigest() {
|
protected HMac getHMAC() {
|
||||||
return new SHA256Digest();
|
return new HMac(new SHA256Digest());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ScramSha256(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
@Override
|
||||||
super(tagWriter, account, rng);
|
protected Digest getDigest() {
|
||||||
}
|
return new SHA256Digest();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
public ScramSha256(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
||||||
public int getPriority() {
|
super(tagWriter, account, rng);
|
||||||
return 25;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMechanism() {
|
public int getPriority() {
|
||||||
return "SCRAM-SHA-256";
|
return 25;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMechanism() {
|
||||||
|
return MECHANISM;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package eu.siacs.conversations.crypto.sasl;
|
||||||
|
|
||||||
|
import org.bouncycastle.crypto.Digest;
|
||||||
|
import org.bouncycastle.crypto.digests.SHA512Digest;
|
||||||
|
import org.bouncycastle.crypto.macs.HMac;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.entities.Account;
|
||||||
|
import eu.siacs.conversations.xml.TagWriter;
|
||||||
|
|
||||||
|
public class ScramSha512 extends ScramMechanism {
|
||||||
|
|
||||||
|
public static final String MECHANISM = "SCRAM-SHA-512";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected HMac getHMAC() {
|
||||||
|
return new HMac(new SHA512Digest());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Digest getDigest() {
|
||||||
|
return new SHA512Digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ScramSha512(final TagWriter tagWriter, final Account account, final SecureRandom rng) {
|
||||||
|
super(tagWriter, account, rng);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getPriority() {
|
||||||
|
return 30;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMechanism() {
|
||||||
|
return MECHANISM;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,69 +10,69 @@ import java.util.NoSuchElementException;
|
||||||
* A tokenizer for GS2 header strings
|
* A tokenizer for GS2 header strings
|
||||||
*/
|
*/
|
||||||
public final class Tokenizer implements Iterator<String>, Iterable<String> {
|
public final class Tokenizer implements Iterator<String>, Iterable<String> {
|
||||||
private final List<String> parts;
|
private final List<String> parts;
|
||||||
private int index;
|
private int index;
|
||||||
|
|
||||||
public Tokenizer(final byte[] challenge) {
|
public Tokenizer(final byte[] challenge) {
|
||||||
final String challengeString = new String(challenge);
|
final String challengeString = new String(challenge);
|
||||||
parts = new ArrayList<>(Arrays.asList(challengeString.split(",")));
|
parts = new ArrayList<>(Arrays.asList(challengeString.split(",")));
|
||||||
// Trim parts.
|
// Trim parts.
|
||||||
for (int i = 0; i < parts.size(); i++) {
|
for (int i = 0; i < parts.size(); i++) {
|
||||||
parts.set(i, parts.get(i).trim());
|
parts.set(i, parts.get(i).trim());
|
||||||
}
|
}
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if there is at least one more element, false otherwise.
|
* Returns true if there is at least one more element, false otherwise.
|
||||||
*
|
*
|
||||||
* @see #next
|
* @see #next
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean hasNext() {
|
public boolean hasNext() {
|
||||||
return parts.size() != index + 1;
|
return parts.size() != index + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the next object and advances the iterator.
|
* Returns the next object and advances the iterator.
|
||||||
*
|
*
|
||||||
* @return the next object.
|
* @return the next object.
|
||||||
* @throws java.util.NoSuchElementException if there are no more elements.
|
* @throws java.util.NoSuchElementException if there are no more elements.
|
||||||
* @see #hasNext
|
* @see #hasNext
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String next() {
|
public String next() {
|
||||||
if (hasNext()) {
|
if (hasNext()) {
|
||||||
return parts.get(index++);
|
return parts.get(index++);
|
||||||
} else {
|
} else {
|
||||||
throw new NoSuchElementException("No such element. Size is: " + parts.size());
|
throw new NoSuchElementException("No such element. Size is: " + parts.size());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the last object returned by {@code next} from the collection.
|
* Removes the last object returned by {@code next} from the collection.
|
||||||
* This method can only be called once between each call to {@code next}.
|
* This method can only be called once between each call to {@code next}.
|
||||||
*
|
*
|
||||||
* @throws UnsupportedOperationException if removing is not supported by the collection being
|
* @throws UnsupportedOperationException if removing is not supported by the collection being
|
||||||
* iterated.
|
* iterated.
|
||||||
* @throws IllegalStateException if {@code next} has not been called, or {@code remove} has
|
* @throws IllegalStateException if {@code next} has not been called, or {@code remove} has
|
||||||
* already been called after the last call to {@code next}.
|
* already been called after the last call to {@code next}.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void remove() {
|
public void remove() {
|
||||||
if(index <= 0) {
|
if (index <= 0) {
|
||||||
throw new IllegalStateException("You can't delete an element before first next() method call");
|
throw new IllegalStateException("You can't delete an element before first next() method call");
|
||||||
}
|
}
|
||||||
parts.remove(--index);
|
parts.remove(--index);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an {@link java.util.Iterator} for the elements in this object.
|
* Returns an {@link java.util.Iterator} for the elements in this object.
|
||||||
*
|
*
|
||||||
* @return An {@code Iterator} instance.
|
* @return An {@code Iterator} instance.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Iterator<String> iterator() {
|
public Iterator<String> iterator() {
|
||||||
return parts.iterator();
|
return parts.iterator();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,7 @@ import eu.siacs.conversations.crypto.sasl.Plain;
|
||||||
import eu.siacs.conversations.crypto.sasl.SaslMechanism;
|
import eu.siacs.conversations.crypto.sasl.SaslMechanism;
|
||||||
import eu.siacs.conversations.crypto.sasl.ScramSha1;
|
import eu.siacs.conversations.crypto.sasl.ScramSha1;
|
||||||
import eu.siacs.conversations.crypto.sasl.ScramSha256;
|
import eu.siacs.conversations.crypto.sasl.ScramSha256;
|
||||||
|
import eu.siacs.conversations.crypto.sasl.ScramSha512;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Message;
|
import eu.siacs.conversations.entities.Message;
|
||||||
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
|
import eu.siacs.conversations.entities.ServiceDiscoveryResult;
|
||||||
|
@ -870,20 +871,21 @@ public class XmppConnection implements Runnable {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authenticate() throws IOException {
|
private void authenticate() throws IOException {
|
||||||
final List<String> mechanisms = extractMechanisms(streamFeatures
|
final List<String> mechanisms = extractMechanisms(streamFeatures.findChild("mechanisms"));
|
||||||
.findChild("mechanisms"));
|
|
||||||
final Element auth = new Element("auth", Namespace.SASL);
|
final Element auth = new Element("auth", Namespace.SASL);
|
||||||
if (mechanisms.contains("EXTERNAL") && account.getPrivateKeyAlias() != null) {
|
if (mechanisms.contains(External.MECHANISM) && account.getPrivateKeyAlias() != null) {
|
||||||
saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG());
|
saslMechanism = new External(tagWriter, account, mXmppConnectionService.getRNG());
|
||||||
} else if (mechanisms.contains("SCRAM-SHA-256")) {
|
} else if (mechanisms.contains(ScramSha512.MECHANISM)) {
|
||||||
|
saslMechanism = new ScramSha512(tagWriter, account, mXmppConnectionService.getRNG());
|
||||||
|
} else if (mechanisms.contains(ScramSha256.MECHANISM)) {
|
||||||
saslMechanism = new ScramSha256(tagWriter, account, mXmppConnectionService.getRNG());
|
saslMechanism = new ScramSha256(tagWriter, account, mXmppConnectionService.getRNG());
|
||||||
} else if (mechanisms.contains("SCRAM-SHA-1")) {
|
} else if (mechanisms.contains(ScramSha1.MECHANISM)) {
|
||||||
saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
|
saslMechanism = new ScramSha1(tagWriter, account, mXmppConnectionService.getRNG());
|
||||||
} else if (mechanisms.contains("PLAIN") && !account.getJid().getDomain().toEscapedString().equals("nimbuzz.com")) {
|
} else if (mechanisms.contains(Plain.MECHANISM) && !account.getJid().getDomain().toEscapedString().equals("nimbuzz.com")) {
|
||||||
saslMechanism = new Plain(tagWriter, account);
|
saslMechanism = new Plain(tagWriter, account);
|
||||||
} else if (mechanisms.contains("DIGEST-MD5")) {
|
} else if (mechanisms.contains(DigestMd5.MECHANISM)) {
|
||||||
saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
|
saslMechanism = new DigestMd5(tagWriter, account, mXmppConnectionService.getRNG());
|
||||||
} else if (mechanisms.contains("ANONYMOUS")) {
|
} else if (mechanisms.contains(Anonymous.MECHANISM)) {
|
||||||
saslMechanism = new Anonymous(tagWriter, account, mXmppConnectionService.getRNG());
|
saslMechanism = new Anonymous(tagWriter, account, mXmppConnectionService.getRNG());
|
||||||
}
|
}
|
||||||
if (saslMechanism != null) {
|
if (saslMechanism != null) {
|
||||||
|
@ -1265,27 +1267,27 @@ public class XmppConnection implements Runnable {
|
||||||
request.setTo(account.getDomain());
|
request.setTo(account.getDomain());
|
||||||
request.addChild("query", Namespace.DISCO_ITEMS).setAttribute("node", Namespace.COMMANDS);
|
request.addChild("query", Namespace.DISCO_ITEMS).setAttribute("node", Namespace.COMMANDS);
|
||||||
sendIqPacket(request, (account, response) -> {
|
sendIqPacket(request, (account, response) -> {
|
||||||
if (response.getType() == IqPacket.TYPE.RESULT) {
|
if (response.getType() == IqPacket.TYPE.RESULT) {
|
||||||
final Element query = response.findChild("query",Namespace.DISCO_ITEMS);
|
final Element query = response.findChild("query", Namespace.DISCO_ITEMS);
|
||||||
if (query == null) {
|
if (query == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
final HashMap<String, Jid> commands = new HashMap<>();
|
final HashMap<String, Jid> commands = new HashMap<>();
|
||||||
for(final Element child : query.getChildren()) {
|
for (final Element child : query.getChildren()) {
|
||||||
if ("item".equals(child.getName())) {
|
if ("item".equals(child.getName())) {
|
||||||
final String node = child.getAttribute("node");
|
final String node = child.getAttribute("node");
|
||||||
final Jid jid = child.getAttributeAsJid("jid");
|
final Jid jid = child.getAttributeAsJid("jid");
|
||||||
if (node != null && jid != null) {
|
if (node != null && jid != null) {
|
||||||
commands.put(node, jid);
|
commands.put(node, jid);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG,commands.toString());
|
Log.d(Config.LOGTAG, commands.toString());
|
||||||
synchronized (this.commands) {
|
synchronized (this.commands) {
|
||||||
this.commands.clear();
|
this.commands.clear();
|
||||||
this.commands.putAll(commands);
|
this.commands.putAll(commands);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue