homogenize ID generation
This commit is contained in:
parent
7ee3e07946
commit
2c32f9738c
|
@ -2,9 +2,12 @@ package im.conversations.android;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import im.conversations.android.xmpp.ConnectionPool;
|
import im.conversations.android.xmpp.ConnectionPool;
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
|
||||||
public class Conversations extends Application {
|
public class Conversations extends Application {
|
||||||
|
|
||||||
|
public static final SecureRandom SECURE_RANDOM = new SecureRandom();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
|
|
68
src/main/java/im/conversations/android/IDs.java
Normal file
68
src/main/java/im/conversations/android/IDs.java
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
package im.conversations.android;
|
||||||
|
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.io.BaseEncoding;
|
||||||
|
import com.google.common.io.ByteSource;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
public class IDs {
|
||||||
|
|
||||||
|
private static final long UUID_VERSION_MASK = 4 << 12;
|
||||||
|
|
||||||
|
public static String medium() {
|
||||||
|
final var random = new byte[9];
|
||||||
|
Conversations.SECURE_RANDOM.nextBytes(random);
|
||||||
|
return BaseEncoding.base64Url().encode(random);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String tiny() {
|
||||||
|
final var random = new byte[3];
|
||||||
|
Conversations.SECURE_RANDOM.nextBytes(random);
|
||||||
|
return BaseEncoding.base64Url().encode(random);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String tiny(final byte[] seed) {
|
||||||
|
return BaseEncoding.base64Url().encode(slice(seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] slice(final byte[] input) {
|
||||||
|
if (input == null || input.length < 3) {
|
||||||
|
return new byte[3];
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return ByteSource.wrap(input).slice(0, 3).read();
|
||||||
|
} catch (final IOException e) {
|
||||||
|
return new byte[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static UUID uuid(final byte[] bytes) {
|
||||||
|
Preconditions.checkArgument(bytes != null && bytes.length == 32);
|
||||||
|
|
||||||
|
long msb = 0;
|
||||||
|
long lsb = 0;
|
||||||
|
|
||||||
|
msb |= (bytes[0x0] & 0xffL) << 56;
|
||||||
|
msb |= (bytes[0x1] & 0xffL) << 48;
|
||||||
|
msb |= (bytes[0x2] & 0xffL) << 40;
|
||||||
|
msb |= (bytes[0x3] & 0xffL) << 32;
|
||||||
|
msb |= (bytes[0x4] & 0xffL) << 24;
|
||||||
|
msb |= (bytes[0x5] & 0xffL) << 16;
|
||||||
|
msb |= (bytes[0x6] & 0xffL) << 8;
|
||||||
|
msb |= (bytes[0x7] & 0xffL);
|
||||||
|
|
||||||
|
lsb |= (bytes[0x8] & 0xffL) << 56;
|
||||||
|
lsb |= (bytes[0x9] & 0xffL) << 48;
|
||||||
|
lsb |= (bytes[0xa] & 0xffL) << 40;
|
||||||
|
lsb |= (bytes[0xb] & 0xffL) << 32;
|
||||||
|
lsb |= (bytes[0xc] & 0xffL) << 24;
|
||||||
|
lsb |= (bytes[0xd] & 0xffL) << 16;
|
||||||
|
lsb |= (bytes[0xe] & 0xffL) << 8;
|
||||||
|
lsb |= (bytes[0xf] & 0xffL);
|
||||||
|
|
||||||
|
msb = (msb & 0xffffffffffff0fffL) | UUID_VERSION_MASK; // set version
|
||||||
|
lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // set variant
|
||||||
|
return new UUID(msb, lsb);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,38 +0,0 @@
|
||||||
package im.conversations.android;
|
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
public class Uuids {
|
|
||||||
|
|
||||||
private static final long VERSION_MASK = 4 << 12;
|
|
||||||
|
|
||||||
public static UUID getUuid(final byte[] bytes) {
|
|
||||||
Preconditions.checkArgument(bytes != null && bytes.length == 32);
|
|
||||||
|
|
||||||
long msb = 0;
|
|
||||||
long lsb = 0;
|
|
||||||
|
|
||||||
msb |= (bytes[0x0] & 0xffL) << 56;
|
|
||||||
msb |= (bytes[0x1] & 0xffL) << 48;
|
|
||||||
msb |= (bytes[0x2] & 0xffL) << 40;
|
|
||||||
msb |= (bytes[0x3] & 0xffL) << 32;
|
|
||||||
msb |= (bytes[0x4] & 0xffL) << 24;
|
|
||||||
msb |= (bytes[0x5] & 0xffL) << 16;
|
|
||||||
msb |= (bytes[0x6] & 0xffL) << 8;
|
|
||||||
msb |= (bytes[0x7] & 0xffL);
|
|
||||||
|
|
||||||
lsb |= (bytes[0x8] & 0xffL) << 56;
|
|
||||||
lsb |= (bytes[0x9] & 0xffL) << 48;
|
|
||||||
lsb |= (bytes[0xa] & 0xffL) << 40;
|
|
||||||
lsb |= (bytes[0xb] & 0xffL) << 32;
|
|
||||||
lsb |= (bytes[0xc] & 0xffL) << 24;
|
|
||||||
lsb |= (bytes[0xd] & 0xffL) << 16;
|
|
||||||
lsb |= (bytes[0xe] & 0xffL) << 8;
|
|
||||||
lsb |= (bytes[0xf] & 0xffL);
|
|
||||||
|
|
||||||
msb = (msb & 0xffffffffffff0fffL) | VERSION_MASK; // set version
|
|
||||||
lsb = (lsb & 0x3fffffffffffffffL) | 0x8000000000000000L; // set variant
|
|
||||||
return new UUID(msb, lsb);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ import com.google.common.base.Preconditions;
|
||||||
import com.google.common.hash.Hashing;
|
import com.google.common.hash.Hashing;
|
||||||
import com.google.common.io.ByteSource;
|
import com.google.common.io.ByteSource;
|
||||||
import eu.siacs.conversations.xmpp.Jid;
|
import eu.siacs.conversations.xmpp.Jid;
|
||||||
import im.conversations.android.Uuids;
|
import im.conversations.android.IDs;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ public class Account {
|
||||||
|
|
||||||
public UUID getPublicDeviceId() {
|
public UUID getPublicDeviceId() {
|
||||||
try {
|
try {
|
||||||
return Uuids.getUuid(
|
return IDs.uuid(
|
||||||
ByteSource.wrap(randomSeed).slice(0, 16).hash(Hashing.sha256()).asBytes());
|
ByteSource.wrap(randomSeed).slice(0, 16).hash(Hashing.sha256()).asBytes());
|
||||||
} catch (final IOException e) {
|
} catch (final IOException e) {
|
||||||
return UUID.randomUUID();
|
return UUID.randomUUID();
|
||||||
|
|
|
@ -16,7 +16,6 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.io.ByteSource;
|
|
||||||
import eu.siacs.conversations.Config;
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.R;
|
import eu.siacs.conversations.R;
|
||||||
import eu.siacs.conversations.crypto.XmppDomainVerifier;
|
import eu.siacs.conversations.crypto.XmppDomainVerifier;
|
||||||
|
@ -27,7 +26,6 @@ import eu.siacs.conversations.persistance.FileBackend;
|
||||||
import eu.siacs.conversations.services.MemorizingTrustManager;
|
import eu.siacs.conversations.services.MemorizingTrustManager;
|
||||||
import eu.siacs.conversations.services.MessageArchiveService;
|
import eu.siacs.conversations.services.MessageArchiveService;
|
||||||
import eu.siacs.conversations.services.NotificationService;
|
import eu.siacs.conversations.services.NotificationService;
|
||||||
import eu.siacs.conversations.utils.CryptoHelper;
|
|
||||||
import eu.siacs.conversations.utils.Patterns;
|
import eu.siacs.conversations.utils.Patterns;
|
||||||
import eu.siacs.conversations.utils.PhoneHelper;
|
import eu.siacs.conversations.utils.PhoneHelper;
|
||||||
import eu.siacs.conversations.utils.Resolver;
|
import eu.siacs.conversations.utils.Resolver;
|
||||||
|
@ -56,6 +54,7 @@ import eu.siacs.conversations.xmpp.stanzas.streammgmt.AckPacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket;
|
import eu.siacs.conversations.xmpp.stanzas.streammgmt.EnablePacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
|
import eu.siacs.conversations.xmpp.stanzas.streammgmt.RequestPacket;
|
||||||
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
|
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
|
||||||
|
import im.conversations.android.IDs;
|
||||||
import im.conversations.android.database.ConversationsDatabase;
|
import im.conversations.android.database.ConversationsDatabase;
|
||||||
import im.conversations.android.database.CredentialStore;
|
import im.conversations.android.database.CredentialStore;
|
||||||
import im.conversations.android.database.model.Account;
|
import im.conversations.android.database.model.Account;
|
||||||
|
@ -1713,6 +1712,7 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
private void sendBindRequest() {
|
private void sendBindRequest() {
|
||||||
clearIqCallbacks();
|
clearIqCallbacks();
|
||||||
|
// TODO if we never store a 'broken' resource we don’t need to fix it
|
||||||
final String recentResource =
|
final String recentResource =
|
||||||
fixResource(
|
fixResource(
|
||||||
ConversationsDatabase.getInstance(context)
|
ConversationsDatabase.getInstance(context)
|
||||||
|
@ -1722,7 +1722,7 @@ public class XmppConnection implements Runnable {
|
||||||
if (recentResource != null) {
|
if (recentResource != null) {
|
||||||
resource = recentResource;
|
resource = recentResource;
|
||||||
} else {
|
} else {
|
||||||
resource = this.createNewResource(account.randomSeed);
|
resource = this.createNewResource(IDs.tiny(account.randomSeed));
|
||||||
}
|
}
|
||||||
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
|
||||||
iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource);
|
iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource);
|
||||||
|
@ -1783,8 +1783,7 @@ public class XmppConnection implements Runnable {
|
||||||
if (packet.getType() == IqPacket.TYPE.ERROR
|
if (packet.getType() == IqPacket.TYPE.ERROR
|
||||||
&& error != null
|
&& error != null
|
||||||
&& error.hasChild("conflict")) {
|
&& error.hasChild("conflict")) {
|
||||||
final String alternativeResource =
|
final String alternativeResource = createNewResource(IDs.tiny());
|
||||||
createNewResource(SECURE_RANDOM.generateSeed(3));
|
|
||||||
ConversationsDatabase.getInstance(context)
|
ConversationsDatabase.getInstance(context)
|
||||||
.accountDao()
|
.accountDao()
|
||||||
.setResource(account.id, alternativeResource);
|
.setResource(account.id, alternativeResource);
|
||||||
|
@ -2132,7 +2131,7 @@ public class XmppConnection implements Runnable {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (streamError.hasChild("conflict")) {
|
if (streamError.hasChild("conflict")) {
|
||||||
final String alternativeResource = createNewResource(SECURE_RANDOM.generateSeed(3));
|
final String alternativeResource = createNewResource(IDs.tiny());
|
||||||
ConversationsDatabase.getInstance(context)
|
ConversationsDatabase.getInstance(context)
|
||||||
.accountDao()
|
.accountDao()
|
||||||
.setResource(account.id, alternativeResource);
|
.setResource(account.id, alternativeResource);
|
||||||
|
@ -2224,31 +2223,8 @@ public class XmppConnection implements Runnable {
|
||||||
tagWriter.writeTag(stream, flush);
|
tagWriter.writeTag(stream, flush);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String createNewResource(final byte[] random) {
|
private String createNewResource(final String postfixId) {
|
||||||
return String.format(
|
return String.format("%s.%s", context.getString(R.string.app_name), postfixId);
|
||||||
"%s.%s",
|
|
||||||
context.getString(R.string.app_name),
|
|
||||||
Base64.encodeToString(
|
|
||||||
slice(random), Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static byte[] slice(final byte[] input) {
|
|
||||||
if (input == null || input.length < 3) {
|
|
||||||
return new byte[3];
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return ByteSource.wrap(input).slice(0, 3).read();
|
|
||||||
} catch (final IOException e) {
|
|
||||||
return new byte[3];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private String nextRandomId() {
|
|
||||||
return nextRandomId(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private String nextRandomId(final boolean s) {
|
|
||||||
return CryptoHelper.random(s ? 3 : 9);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String sendIqPacket(final IqPacket packet, final Consumer<IqPacket> callback) {
|
public String sendIqPacket(final IqPacket packet, final Consumer<IqPacket> callback) {
|
||||||
|
@ -2258,8 +2234,8 @@ public class XmppConnection implements Runnable {
|
||||||
|
|
||||||
public synchronized String sendUnmodifiedIqPacket(
|
public synchronized String sendUnmodifiedIqPacket(
|
||||||
final IqPacket packet, final Consumer<IqPacket> callback, boolean force) {
|
final IqPacket packet, final Consumer<IqPacket> callback, boolean force) {
|
||||||
if (packet.getId() == null) {
|
if (Strings.isNullOrEmpty(packet.getId())) {
|
||||||
packet.setAttribute("id", nextRandomId());
|
packet.setAttribute("id", IDs.medium());
|
||||||
}
|
}
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
synchronized (this.packetCallbacks) {
|
synchronized (this.packetCallbacks) {
|
||||||
|
|
Loading…
Reference in a new issue