Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/iNPUTmice/Conversations.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Gultsch <daniel@gultsch.de>2022-10-15 19:56:31 +0300
committerDaniel Gultsch <daniel@gultsch.de>2022-10-15 19:56:31 +0300
commit24badda4c9d99f597cb12f24ad8bd236195c7ce2 (patch)
tree5aa5757d1b600050203536cb5b5fce22e2be373a
parentc13787873c0324a1e40ae8c5c4700f297bd4a31f (diff)
do quick start with HT-SHA-256-NONE
-rw-r--r--src/main/java/eu/siacs/conversations/Config.java2
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java2
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBindingMechanism.java6
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java57
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha256.java5
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha512.java5
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java6
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/sasl/ScramPlusMechanism.java3
-rw-r--r--src/main/java/eu/siacs/conversations/entities/Account.java144
-rw-r--r--src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java9
-rw-r--r--src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java60
11 files changed, 237 insertions, 62 deletions
diff --git a/src/main/java/eu/siacs/conversations/Config.java b/src/main/java/eu/siacs/conversations/Config.java
index 27d1f1097..812f6ae10 100644
--- a/src/main/java/eu/siacs/conversations/Config.java
+++ b/src/main/java/eu/siacs/conversations/Config.java
@@ -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;
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java
index 26f9c9da0..216f3d7f8 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBinding.java
@@ -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;
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBindingMechanism.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBindingMechanism.java
new file mode 100644
index 000000000..d4e34ba59
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ChannelBindingMechanism.java
@@ -0,0 +1,6 @@
+package eu.siacs.conversations.crypto.sasl;
+
+public interface ChannelBindingMechanism {
+
+ ChannelBinding getChannelBinding();
+}
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java
index b54864fea..1d8aeac69 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedToken.java
@@ -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;
+ }
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha256.java b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha256.java
index fae756485..aef19d72a 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha256.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha256.java
@@ -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);
}
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha512.java b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha512.java
index fd1b7be51..6f48b5444 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha512.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/HashedTokenSha512.java
@@ -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);
}
}
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java b/src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java
index e0df3a2d4..48835f9df 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/SaslMechanism.java
@@ -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(
diff --git a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramPlusMechanism.java b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramPlusMechanism.java
index 707883d73..c6a63ddbd 100644
--- a/src/main/java/eu/siacs/conversations/crypto/sasl/ScramPlusMechanism.java
+++ b/src/main/java/eu/siacs/conversations/crypto/sasl/ScramPlusMechanism.java
@@ -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;
}
diff --git a/src/main/java/eu/siacs/conversations/entities/Account.java b/src/main/java/eu/siacs/conversations/entities/Account.java
index 1817c24bb..d570cbec3 100644
--- a/src/main/java/eu/siacs/conversations/entities/Account.java
+++ b/src/main/java/eu/siacs/conversations/entities/Account.java
@@ -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);
- }
-
- 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) {
+ 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,
+ 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,
diff --git a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
index 49de553eb..3eb48a1c9 100644
--- a/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
+++ b/src/main/java/eu/siacs/conversations/persistance/DatabaseBackend.java
@@ -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");
}
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 2fa8d6df3..2c60534dd 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -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);