do quick start with HT-SHA-256-NONE
This commit is contained in:
parent
c13787873c
commit
24badda4c9
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
package eu.siacs.conversations.crypto.sasl;
|
||||
|
||||
public interface ChannelBindingMechanism {
|
||||
|
||||
ChannelBinding getChannelBinding();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue