homogenize ID generation

This commit is contained in:
Daniel Gultsch 2023-01-13 16:35:33 +01:00
parent 7ee3e07946
commit 2c32f9738c
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
5 changed files with 82 additions and 73 deletions

View file

@ -2,9 +2,12 @@ package im.conversations.android;
import android.app.Application;
import im.conversations.android.xmpp.ConnectionPool;
import java.security.SecureRandom;
public class Conversations extends Application {
public static final SecureRandom SECURE_RANDOM = new SecureRandom();
@Override
public void onCreate() {
super.onCreate();

View 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);
}
}

View file

@ -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);
}
}

View file

@ -6,7 +6,7 @@ import com.google.common.base.Preconditions;
import com.google.common.hash.Hashing;
import com.google.common.io.ByteSource;
import eu.siacs.conversations.xmpp.Jid;
import im.conversations.android.Uuids;
import im.conversations.android.IDs;
import java.io.IOException;
import java.util.UUID;
@ -48,7 +48,7 @@ public class Account {
public UUID getPublicDeviceId() {
try {
return Uuids.getUuid(
return IDs.uuid(
ByteSource.wrap(randomSeed).slice(0, 16).hash(Hashing.sha256()).asBytes());
} catch (final IOException e) {
return UUID.randomUUID();

View file

@ -16,7 +16,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.io.ByteSource;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
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.MessageArchiveService;
import eu.siacs.conversations.services.NotificationService;
import eu.siacs.conversations.utils.CryptoHelper;
import eu.siacs.conversations.utils.Patterns;
import eu.siacs.conversations.utils.PhoneHelper;
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.RequestPacket;
import eu.siacs.conversations.xmpp.stanzas.streammgmt.ResumePacket;
import im.conversations.android.IDs;
import im.conversations.android.database.ConversationsDatabase;
import im.conversations.android.database.CredentialStore;
import im.conversations.android.database.model.Account;
@ -1713,6 +1712,7 @@ public class XmppConnection implements Runnable {
private void sendBindRequest() {
clearIqCallbacks();
// TODO if we never store a 'broken' resource we dont need to fix it
final String recentResource =
fixResource(
ConversationsDatabase.getInstance(context)
@ -1722,7 +1722,7 @@ public class XmppConnection implements Runnable {
if (recentResource != null) {
resource = recentResource;
} else {
resource = this.createNewResource(account.randomSeed);
resource = this.createNewResource(IDs.tiny(account.randomSeed));
}
final IqPacket iq = new IqPacket(IqPacket.TYPE.SET);
iq.addChild("bind", Namespace.BIND).addChild("resource").setContent(resource);
@ -1783,8 +1783,7 @@ public class XmppConnection implements Runnable {
if (packet.getType() == IqPacket.TYPE.ERROR
&& error != null
&& error.hasChild("conflict")) {
final String alternativeResource =
createNewResource(SECURE_RANDOM.generateSeed(3));
final String alternativeResource = createNewResource(IDs.tiny());
ConversationsDatabase.getInstance(context)
.accountDao()
.setResource(account.id, alternativeResource);
@ -2132,7 +2131,7 @@ public class XmppConnection implements Runnable {
return;
}
if (streamError.hasChild("conflict")) {
final String alternativeResource = createNewResource(SECURE_RANDOM.generateSeed(3));
final String alternativeResource = createNewResource(IDs.tiny());
ConversationsDatabase.getInstance(context)
.accountDao()
.setResource(account.id, alternativeResource);
@ -2224,31 +2223,8 @@ public class XmppConnection implements Runnable {
tagWriter.writeTag(stream, flush);
}
private String createNewResource(final byte[] random) {
return String.format(
"%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);
private String createNewResource(final String postfixId) {
return String.format("%s.%s", context.getString(R.string.app_name), postfixId);
}
public String sendIqPacket(final IqPacket packet, final Consumer<IqPacket> callback) {
@ -2258,8 +2234,8 @@ public class XmppConnection implements Runnable {
public synchronized String sendUnmodifiedIqPacket(
final IqPacket packet, final Consumer<IqPacket> callback, boolean force) {
if (packet.getId() == null) {
packet.setAttribute("id", nextRandomId());
if (Strings.isNullOrEmpty(packet.getId())) {
packet.setAttribute("id", IDs.medium());
}
if (callback != null) {
synchronized (this.packetCallbacks) {