do quick start with HT-SHA-256-NONE

This commit is contained in:
Daniel Gultsch 2022-10-15 18:56:31 +02:00
parent c13787873c
commit 24badda4c9
11 changed files with 235 additions and 60 deletions

View file

@ -60,7 +60,7 @@ public final class Config {
public static final long CONTACT_SYNC_RETRY_INTERVAL = 1000L * 60 * 5;
public static final boolean QUICKSTART_ENABLED = false;
public static final boolean QUICKSTART_ENABLED = true;
//Notification settings
public static final boolean HIDE_MESSAGE_TEXT_IN_NOTIFICATION = false;

View file

@ -97,7 +97,7 @@ public enum ChannelBinding {
}
}
public static boolean ensureBest(
public static boolean isAvailable(
final ChannelBinding channelBinding, final SSLSockets.Version sslVersion) {
return ChannelBinding.best(Collections.singleton(channelBinding), sslVersion)
== channelBinding;

View file

@ -0,0 +1,6 @@
package eu.siacs.conversations.crypto.sasl;
public interface ChannelBindingMechanism {
ChannelBinding getChannelBinding();
}

View file

@ -1,12 +1,17 @@
package eu.siacs.conversations.crypto.sasl;
import android.util.Base64;
import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMultimap;
import com.google.common.collect.Multimap;
import com.google.common.hash.HashFunction;
import com.google.common.primitives.Bytes;
import org.jetbrains.annotations.NotNull;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
@ -16,11 +21,13 @@ import javax.net.ssl.SSLSocket;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.utils.SSLSockets;
public abstract class HashedToken extends SaslMechanism {
public abstract class HashedToken extends SaslMechanism implements ChannelBindingMechanism {
private static final String PREFIX = "HT";
private static final List<String> HASH_FUNCTIONS = Arrays.asList("SHA-512", "SHA-256");
private static final byte[] INITIATOR = "Initiator".getBytes(StandardCharsets.UTF_8);
private static final byte[] RESPONDER = "Responder".getBytes(StandardCharsets.UTF_8);
protected final ChannelBinding channelBinding;
@ -36,18 +43,48 @@ public abstract class HashedToken extends SaslMechanism {
@Override
public String getClientFirstMessage() {
return null; // HMAC(token, "Initiator" || cb-data)
final String token = Strings.nullToEmpty(this.account.getFastToken());
final HashFunction hashing = getHashFunction(token.getBytes(StandardCharsets.UTF_8));
final byte[] cbData = new byte[0];
final byte[] initiatorHashedToken =
hashing.hashBytes(Bytes.concat(INITIATOR, cbData)).asBytes();
final byte[] firstMessage =
Bytes.concat(
account.getUsername().getBytes(StandardCharsets.UTF_8),
new byte[] {0x00},
initiatorHashedToken);
return Base64.encodeToString(firstMessage, Base64.NO_WRAP);
}
@Override
public String getResponse(final String challenge, final SSLSocket socket)
throws AuthenticationException {
// todo verify that challenge matches HMAC(token, "Responder" || cb-data)
return null;
final byte[] responderMessage;
try {
responderMessage = Base64.decode(challenge, Base64.NO_WRAP);
} catch (final Exception e) {
throw new AuthenticationException("Unable to decode responder message", e);
}
final String token = Strings.nullToEmpty(this.account.getFastToken());
final HashFunction hashing = getHashFunction(token.getBytes(StandardCharsets.UTF_8));
final byte[] cbData = new byte[0];
final byte[] expectedResponderMessage =
hashing.hashBytes(Bytes.concat(RESPONDER, cbData)).asBytes();
if (Arrays.equals(responderMessage, expectedResponderMessage)) {
return null;
}
throw new AuthenticationException("Responder message did not match");
}
protected abstract HashFunction getHashFunction(final byte[] key);
public abstract Mechanism getTokenMechanism();
@Override
public String getMechanism() {
return getTokenMechanism().name();
}
public static final class Mechanism {
public final String hashFunction;
public final ChannelBinding channelBinding;
@ -77,6 +114,14 @@ public abstract class HashedToken extends SaslMechanism {
}
}
public static Mechanism ofOrNull(final String mechanism) {
try {
return mechanism == null ? null : of(mechanism);
} catch (final IllegalArgumentException e) {
return null;
}
}
public static Multimap<String, ChannelBinding> of(final Collection<String> mechanisms) {
final ImmutableMultimap.Builder<String, ChannelBinding> builder =
ImmutableMultimap.builder();
@ -119,4 +164,8 @@ public abstract class HashedToken extends SaslMechanism {
PREFIX, hashFunction, ChannelBinding.SHORT_NAMES.get(channelBinding));
}
}
public ChannelBinding getChannelBinding() {
return this.channelBinding;
}
}

View file

@ -17,8 +17,7 @@ public class HashedTokenSha256 extends HashedToken {
}
@Override
public String getMechanism() {
final String cbShortName = ChannelBinding.SHORT_NAMES.get(this.channelBinding);
return String.format("HT-SHA-256-%s", cbShortName);
public Mechanism getTokenMechanism() {
return new Mechanism("SHA-256", channelBinding);
}
}

View file

@ -17,8 +17,7 @@ public class HashedTokenSha512 extends HashedToken {
}
@Override
public String getMechanism() {
final String cbShortName = ChannelBinding.SHORT_NAMES.get(this.channelBinding);
return String.format("HT-SHA-512-%s", cbShortName);
public Mechanism getTokenMechanism() {
return new Mechanism("SHA-512", this.channelBinding);
}
}

View file

@ -166,9 +166,9 @@ public abstract class SaslMechanism {
public static SaslMechanism ensureAvailable(
final SaslMechanism mechanism, final SSLSockets.Version sslVersion) {
if (mechanism instanceof ScramPlusMechanism) {
final ChannelBinding cb = ((ScramPlusMechanism) mechanism).getChannelBinding();
if (ChannelBinding.ensureBest(cb, sslVersion)) {
if (mechanism instanceof ChannelBindingMechanism) {
final ChannelBinding cb = ((ChannelBindingMechanism) mechanism).getChannelBinding();
if (ChannelBinding.isAvailable(cb, sslVersion)) {
return mechanism;
} else {
Log.d(

View file

@ -16,7 +16,7 @@ import javax.net.ssl.SSLSocket;
import eu.siacs.conversations.entities.Account;
public abstract class ScramPlusMechanism extends ScramMechanism {
public abstract class ScramPlusMechanism extends ScramMechanism implements ChannelBindingMechanism {
private static final String EXPORTER_LABEL = "EXPORTER-Channel-Binding";
@ -103,6 +103,7 @@ public abstract class ScramPlusMechanism extends ScramMechanism {
return messageDigest.digest();
}
@Override
public ChannelBinding getChannelBinding() {
return this.channelBinding;
}

View file

@ -26,6 +26,9 @@ import eu.siacs.conversations.crypto.PgpDecryptionService;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.crypto.axolotl.XmppAxolotlSession;
import eu.siacs.conversations.crypto.sasl.ChannelBinding;
import eu.siacs.conversations.crypto.sasl.HashedToken;
import eu.siacs.conversations.crypto.sasl.HashedTokenSha256;
import eu.siacs.conversations.crypto.sasl.HashedTokenSha512;
import eu.siacs.conversations.crypto.sasl.SaslMechanism;
import eu.siacs.conversations.crypto.sasl.ScramPlusMechanism;
import eu.siacs.conversations.services.AvatarService;
@ -55,7 +58,8 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
public static final String RESOURCE = "resource";
public static final String PINNED_MECHANISM = "pinned_mechanism";
public static final String PINNED_CHANNEL_BINDING = "pinned_channel_binding";
public static final String FAST_MECHANISM = "fast_mechanism";
public static final String FAST_TOKEN = "fast_token";
public static final int OPTION_DISABLED = 1;
public static final int OPTION_REGISTER = 2;
@ -72,7 +76,6 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
private static final String KEY_PINNED_MECHANISM = "pinned_mechanism";
public static final String KEY_PRE_AUTH_REGISTRATION_TOKEN = "pre_auth_registration";
protected final JSONObject keys;
private final Roster roster = new Roster(this);
private final Collection<Jid> blocklist = new CopyOnWriteArraySet<>();
@ -101,16 +104,46 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
private String presenceStatusMessage;
private String pinnedMechanism;
private String pinnedChannelBinding;
private String fastMechanism;
private String fastToken;
public Account(final Jid jid, final String password) {
this(java.util.UUID.randomUUID().toString(), jid,
password, 0, null, "", null, null, null, 5222, Presence.Status.ONLINE, null, null, null);
this(
java.util.UUID.randomUUID().toString(),
jid,
password,
0,
null,
"",
null,
null,
null,
5222,
Presence.Status.ONLINE,
null,
null,
null,
null,
null);
}
private Account(final String uuid, final Jid jid,
final String password, final int options, final String rosterVersion, final String keys,
final String avatar, String displayName, String hostname, int port,
final Presence.Status status, String statusMessage, final String pinnedMechanism, final String pinnedChannelBinding) {
private Account(
final String uuid,
final Jid jid,
final String password,
final int options,
final String rosterVersion,
final String keys,
final String avatar,
String displayName,
String hostname,
int port,
final Presence.Status status,
String statusMessage,
final String pinnedMechanism,
final String pinnedChannelBinding,
final String fastMechanism,
final String fastToken) {
this.uuid = uuid;
this.jid = jid;
this.password = password;
@ -131,21 +164,29 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
this.presenceStatusMessage = statusMessage;
this.pinnedMechanism = pinnedMechanism;
this.pinnedChannelBinding = pinnedChannelBinding;
this.fastMechanism = fastMechanism;
this.fastToken = fastToken;
}
public static Account fromCursor(final Cursor cursor) {
final Jid jid;
try {
final String resource = cursor.getString(cursor.getColumnIndexOrThrow(RESOURCE));
jid = Jid.of(
cursor.getString(cursor.getColumnIndexOrThrow(USERNAME)),
cursor.getString(cursor.getColumnIndexOrThrow(SERVER)),
resource == null || resource.trim().isEmpty() ? null : resource);
jid =
Jid.of(
cursor.getString(cursor.getColumnIndexOrThrow(USERNAME)),
cursor.getString(cursor.getColumnIndexOrThrow(SERVER)),
resource == null || resource.trim().isEmpty() ? null : resource);
} catch (final IllegalArgumentException e) {
Log.d(Config.LOGTAG, cursor.getString(cursor.getColumnIndexOrThrow(USERNAME)) + "@" + cursor.getString(cursor.getColumnIndexOrThrow(SERVER)));
Log.d(
Config.LOGTAG,
cursor.getString(cursor.getColumnIndexOrThrow(USERNAME))
+ "@"
+ cursor.getString(cursor.getColumnIndexOrThrow(SERVER)));
throw new AssertionError(e);
}
return new Account(cursor.getString(cursor.getColumnIndexOrThrow(UUID)),
return new Account(
cursor.getString(cursor.getColumnIndexOrThrow(UUID)),
jid,
cursor.getString(cursor.getColumnIndexOrThrow(PASSWORD)),
cursor.getInt(cursor.getColumnIndexOrThrow(OPTIONS)),
@ -155,10 +196,13 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
cursor.getString(cursor.getColumnIndexOrThrow(DISPLAY_NAME)),
cursor.getString(cursor.getColumnIndexOrThrow(HOSTNAME)),
cursor.getInt(cursor.getColumnIndexOrThrow(PORT)),
Presence.Status.fromShowString(cursor.getString(cursor.getColumnIndexOrThrow(STATUS))),
Presence.Status.fromShowString(
cursor.getString(cursor.getColumnIndexOrThrow(STATUS))),
cursor.getString(cursor.getColumnIndexOrThrow(STATUS_MESSAGE)),
cursor.getString(cursor.getColumnIndexOrThrow(PINNED_MECHANISM)),
cursor.getString(cursor.getColumnIndexOrThrow(PINNED_CHANNEL_BINDING)));
cursor.getString(cursor.getColumnIndexOrThrow(PINNED_CHANNEL_BINDING)),
cursor.getString(cursor.getColumnIndexOrThrow(FAST_MECHANISM)),
cursor.getString(cursor.getColumnIndexOrThrow(FAST_TOKEN)));
}
public boolean httpUploadAvailable(long size) {
@ -305,10 +349,18 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
public void setPinnedMechanism(final SaslMechanism mechanism) {
this.pinnedMechanism = mechanism.getMechanism();
if (mechanism instanceof ScramPlusMechanism) {
this.pinnedChannelBinding = ((ScramPlusMechanism) mechanism).getChannelBinding().toString();
this.pinnedChannelBinding =
((ScramPlusMechanism) mechanism).getChannelBinding().toString();
} else {
this.pinnedChannelBinding = null;
}
}
public void setFastToken(final HashedToken.Mechanism mechanism, final String token) {
this.fastMechanism = mechanism.name();
this.fastToken = token;
}
public void resetPinnedMechanism() {
this.pinnedMechanism = null;
this.pinnedChannelBinding = null;
@ -328,12 +380,39 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
}
}
public SaslMechanism getPinnedMechanism() {
private SaslMechanism getPinnedMechanism() {
final String mechanism = Strings.nullToEmpty(this.pinnedMechanism);
final ChannelBinding channelBinding = ChannelBinding.get(this.pinnedChannelBinding);
return new SaslMechanism.Factory(this).of(mechanism, channelBinding);
}
private HashedToken getFastMechanism() {
final HashedToken.Mechanism fastMechanism = HashedToken.Mechanism.ofOrNull(this.fastMechanism);
final String token = this.fastToken;
if (fastMechanism == null || Strings.isNullOrEmpty(token)) {
return null;
}
if (fastMechanism.hashFunction.equals("SHA-256")) {
return new HashedTokenSha256(this, fastMechanism.channelBinding);
} else if (fastMechanism.hashFunction.equals("SHA-512")) {
return new HashedTokenSha512(this, fastMechanism.channelBinding);
} else {
return null;
}
}
public SaslMechanism getQuickStartMechanism() {
final HashedToken hashedTokenMechanism = getFastMechanism();
if (hashedTokenMechanism != null) {
return hashedTokenMechanism;
}
return getPinnedMechanism();
}
public String getFastToken() {
return this.fastToken;
}
public State getTrueStatus() {
return this.status;
}
@ -435,6 +514,8 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
values.put(RESOURCE, jid.getResource());
values.put(PINNED_MECHANISM, pinnedMechanism);
values.put(PINNED_CHANNEL_BINDING, pinnedChannelBinding);
values.put(FAST_MECHANISM, this.fastMechanism);
values.put(FAST_TOKEN, this.fastToken);
return values;
}
@ -480,7 +561,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
public int activeDevicesWithRtpCapability() {
int i = 0;
for(Presence presence : getSelfContact().getPresences().getPresences()) {
for (Presence presence : getSelfContact().getPresences().getPresences()) {
if (RtpCapability.check(presence) != RtpCapability.Capability.NONE) {
i++;
}
@ -617,7 +698,9 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
public String getShareableLink() {
List<XmppUri.Fingerprint> fingerprints = this.getFingerprints();
String uri = "https://conversations.im/i/" + XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
String uri =
"https://conversations.im/i/"
+ XmppUri.lameUrlEncode(this.getJid().asBareJid().toEscapedString());
if (fingerprints.size() > 0) {
return XmppUri.getFingerprintUri(uri, fingerprints, '&');
} else {
@ -630,10 +713,18 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
if (axolotlService == null) {
return fingerprints;
}
fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, axolotlService.getOwnFingerprint().substring(2), axolotlService.getOwnDeviceId()));
fingerprints.add(
new XmppUri.Fingerprint(
XmppUri.FingerprintType.OMEMO,
axolotlService.getOwnFingerprint().substring(2),
axolotlService.getOwnDeviceId()));
for (XmppAxolotlSession session : axolotlService.findOwnSessions()) {
if (session.getTrust().isVerified() && session.getTrust().isActive()) {
fingerprints.add(new XmppUri.Fingerprint(XmppUri.FingerprintType.OMEMO, session.getFingerprint().substring(2).replaceAll("\\s", ""), session.getRemoteAddress().getDeviceId()));
fingerprints.add(
new XmppUri.Fingerprint(
XmppUri.FingerprintType.OMEMO,
session.getFingerprint().substring(2).replaceAll("\\s", ""),
session.getRemoteAddress().getDeviceId()));
}
}
return fingerprints;
@ -641,7 +732,8 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
public boolean isBlocked(final ListItem contact) {
final Jid jid = contact.getJid();
return jid != null && (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
return jid != null
&& (blocklist.contains(jid.asBareJid()) || blocklist.contains(jid.getDomain()));
}
public boolean isBlocked(final Jid jid) {
@ -685,7 +777,7 @@ public class Account extends AbstractEntity implements AvatarService.Avatarable
REGISTRATION_CONFLICT(true, false),
REGISTRATION_NOT_SUPPORTED(true, false),
REGISTRATION_PLEASE_WAIT(true, false),
REGISTRATION_INVALID_TOKEN(true,false),
REGISTRATION_INVALID_TOKEN(true, false),
REGISTRATION_PASSWORD_TOO_WEAK(true, false),
TLS_ERROR,
TLS_ERROR_DOMAIN,

View file

@ -64,7 +64,7 @@ import eu.siacs.conversations.xmpp.mam.MamReference;
public class DatabaseBackend extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "history";
private static final int DATABASE_VERSION = 50;
private static final int DATABASE_VERSION = 51;
private static boolean requiresMessageIndexRebuild = false;
private static DatabaseBackend instance = null;
@ -232,6 +232,8 @@ public class DatabaseBackend extends SQLiteOpenHelper {
+ Account.RESOURCE + " TEXT,"
+ Account.PINNED_MECHANISM + " TEXT,"
+ Account.PINNED_CHANNEL_BINDING + " TEXT,"
+ Account.FAST_MECHANISM + " TEXT,"
+ Account.FAST_TOKEN + " TEXT,"
+ Account.PORT + " NUMBER DEFAULT 5222)");
db.execSQL("create table " + Conversation.TABLENAME + " ("
+ Conversation.UUID + " TEXT PRIMARY KEY, " + Conversation.NAME
@ -594,7 +596,10 @@ public class DatabaseBackend extends SQLiteOpenHelper {
if (oldVersion < 50 && newVersion >= 50) {
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PINNED_MECHANISM + " TEXT");
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.PINNED_CHANNEL_BINDING + " TEXT");
}
if (oldVersion < 51 && newVersion >= 51) {
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.FAST_MECHANISM + " TEXT");
db.execSQL("ALTER TABLE " + Account.TABLENAME + " ADD COLUMN " + Account.FAST_TOKEN + " TEXT");
}
}

View file

@ -757,7 +757,6 @@ public class XmppConnection implements Runnable {
Config.LOGTAG,
account.getJid().asBareJid()
+ ": jid changed during SASL 2.0. updating database");
mXmppConnectionService.databaseBackend.updateAccount(account);
}
final Element bound = success.findChild("bound", Namespace.BIND2);
final Element resumed = success.findChild("resumed", "urn:xmpp:sm:3");
@ -798,11 +797,21 @@ public class XmppConnection implements Runnable {
}
sendPostBindInitialization(waitForDisco, carbonsEnabled != null);
}
//TODO figure out name either by the existence of hashTokenRequest or if scramMechanism is of instance HashedToken
if (this.hashTokenRequest != null && !Strings.isNullOrEmpty(token)) {
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": storing hashed token "+this.hashTokenRequest.name()+ " "+token);
final HashedToken.Mechanism tokenMechanism;
final SaslMechanism currentMechanism = this.saslMechanism;
if (currentMechanism instanceof HashedToken) {
tokenMechanism = ((HashedToken) currentMechanism).getTokenMechanism();
} else if (this.hashTokenRequest != null) {
tokenMechanism = this.hashTokenRequest;
} else {
tokenMechanism = null;
}
if (tokenMechanism != null && !Strings.isNullOrEmpty(token)) {
this.account.setFastToken(tokenMechanism,token);
Log.d(Config.LOGTAG,account.getJid().asBareJid()+": storing hashed token "+tokenMechanism);
}
}
mXmppConnectionService.databaseBackend.updateAccount(account);
this.quickStartInProgress = false;
if (version == SaslMechanism.Version.SASL) {
tagReader.reset();
@ -826,6 +835,7 @@ public class XmppConnection implements Runnable {
} catch (final IllegalArgumentException e) {
throw new StateChangingException(Account.State.INCOMPATIBLE_SERVER);
}
Log.d(Config.LOGTAG,failure.toString());
Log.d(Config.LOGTAG, account.getJid().asBareJid() + ": login failure " + version);
if (failure.hasChild("temporary-auth-failure")) {
throw new StateChangingException(Account.State.TEMPORARY_AUTH_FAILURE);
@ -1340,6 +1350,7 @@ public class XmppConnection implements Runnable {
}
final boolean quickStartAvailable;
final String firstMessage = saslMechanism.getClientFirstMessage();
final boolean usingFast = saslMechanism instanceof HashedToken;
final Element authenticate;
if (version == SaslMechanism.Version.SASL) {
authenticate = new Element("auth", Namespace.SASL);
@ -1350,9 +1361,15 @@ public class XmppConnection implements Runnable {
} else if (version == SaslMechanism.Version.SASL_2) {
final Element inline = authElement.findChild("inline", Namespace.SASL_2);
final boolean sm = inline != null && inline.hasChild("sm", "urn:xmpp:sm:3");
final Element fast = inline == null ? null : inline.findChild("fast", Namespace.FAST);
final Collection<String> fastMechanisms = SaslMechanism.mechanisms(fast);
final HashedToken.Mechanism hashTokenRequest = HashedToken.Mechanism.best(fastMechanisms, SSLSockets.version(this.socket));
final HashedToken.Mechanism hashTokenRequest;
if (usingFast) {
hashTokenRequest = null;
} else {
final Element fast = inline == null ? null : inline.findChild("fast", Namespace.FAST);
final Collection<String> fastMechanisms = SaslMechanism.mechanisms(fast);
hashTokenRequest =
HashedToken.Mechanism.best(fastMechanisms, SSLSockets.version(this.socket));
}
final Collection<String> bindFeatures = Bind2.features(inline);
quickStartAvailable =
sm
@ -1370,7 +1387,7 @@ public class XmppConnection implements Runnable {
}
}
this.hashTokenRequest = hashTokenRequest;
authenticate = generateAuthenticationRequest(firstMessage, hashTokenRequest, bindFeatures, sm);
authenticate = generateAuthenticationRequest(firstMessage, usingFast, hashTokenRequest, bindFeatures, sm);
} else {
throw new AssertionError("Missing implementation for " + version);
}
@ -1390,12 +1407,13 @@ public class XmppConnection implements Runnable {
tagWriter.writeElement(authenticate);
}
private Element generateAuthenticationRequest(final String firstMessage) {
return generateAuthenticationRequest(firstMessage, null, Bind2.QUICKSTART_FEATURES, true);
private Element generateAuthenticationRequest(final String firstMessage, final boolean usingFast) {
return generateAuthenticationRequest(firstMessage, usingFast, null, Bind2.QUICKSTART_FEATURES, true);
}
private Element generateAuthenticationRequest(
final String firstMessage,
final boolean usingFast,
final HashedToken.Mechanism hashedTokenRequest,
final Collection<String> bind,
final boolean inlineStreamManagement) {
@ -1423,7 +1441,12 @@ public class XmppConnection implements Runnable {
authenticate.addChild(resume);
}
if (hashedTokenRequest != null) {
authenticate.addChild("request-token", Namespace.FAST).setAttribute("mechanism", hashedTokenRequest.name());
authenticate
.addChild("request-token", Namespace.FAST)
.setAttribute("mechanism", hashedTokenRequest.name());
}
if (usingFast) {
authenticate.addChild("fast", Namespace.FAST);
}
return authenticate;
}
@ -2059,25 +2082,26 @@ public class XmppConnection implements Runnable {
private boolean establishStream(final SSLSockets.Version sslVersion)
throws IOException, InterruptedException {
final SaslMechanism pinnedMechanism =
SaslMechanism.ensureAvailable(account.getPinnedMechanism(), sslVersion);
final SaslMechanism quickStartMechanism =
SaslMechanism.ensureAvailable(account.getQuickStartMechanism(), sslVersion);
final boolean secureConnection = sslVersion != SSLSockets.Version.NONE;
if (secureConnection
&& Config.QUICKSTART_ENABLED
&& pinnedMechanism != null
&& quickStartMechanism != null
&& account.isOptionSet(Account.OPTION_QUICKSTART_AVAILABLE)) {
mXmppConnectionService.restoredFromDatabaseLatch.await();
this.saslMechanism = pinnedMechanism;
this.saslMechanism = quickStartMechanism;
final boolean usingFast = quickStartMechanism instanceof HashedToken;
final Element authenticate =
generateAuthenticationRequest(pinnedMechanism.getClientFirstMessage());
authenticate.setAttribute("mechanism", pinnedMechanism.getMechanism());
generateAuthenticationRequest(quickStartMechanism.getClientFirstMessage(), usingFast);
authenticate.setAttribute("mechanism", quickStartMechanism.getMechanism());
sendStartStream(true, false);
tagWriter.writeElement(authenticate);
Log.d(
Config.LOGTAG,
account.getJid().toString()
+ ": quick start with "
+ pinnedMechanism.getMechanism());
+ quickStartMechanism.getMechanism());
return true;
} else {
sendStartStream(secureConnection, true);