use dedicated hash object instead of byte[] for caps

this way we can store the algo alongside the object
This commit is contained in:
Daniel Gultsch 2023-01-17 08:53:21 +01:00
parent 6458c6e9f9
commit a2b21d97eb
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
4 changed files with 84 additions and 14 deletions

View file

@ -5,6 +5,7 @@ import com.google.common.collect.Collections2;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
import com.google.common.hash.Hashing;
import com.google.common.io.BaseEncoding;
import im.conversations.android.xmpp.model.data.Data;
import im.conversations.android.xmpp.model.data.Field;
import im.conversations.android.xmpp.model.disco.info.Feature;
@ -15,7 +16,7 @@ import java.util.Comparator;
import java.util.List;
public final class EntityCapabilities {
public static byte[] hash(final InfoQuery info) {
public static EntityCapsHash hash(final InfoQuery info) {
final StringBuilder s = new StringBuilder();
final List<Identity> orderedIdentities =
Ordering.from(
@ -76,7 +77,8 @@ public final class EntityCapabilities {
}
}
}
return Hashing.sha1().hashString(s.toString(), StandardCharsets.UTF_8).asBytes();
return new EntityCapsHash(
Hashing.sha1().hashString(s.toString(), StandardCharsets.UTF_8).asBytes());
}
private static String clean(String s) {
@ -86,4 +88,23 @@ public final class EntityCapabilities {
private static String blankNull(String s) {
return s == null ? "" : clean(s);
}
public abstract static class Hash {
public final byte[] hash;
protected Hash(byte[] hash) {
this.hash = hash;
}
public String encoded() {
return BaseEncoding.base64().encode(hash);
}
}
public static class EntityCapsHash extends Hash {
protected EntityCapsHash(byte[] hash) {
super(hash);
}
}
}

View file

@ -1,5 +1,8 @@
package im.conversations.android.xmpp;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.common.base.CaseFormat;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Collections2;
@ -25,13 +28,28 @@ public class EntityCapabilities2 {
private static final char FILE_SEPARATOR = 0x1c;
public static byte[] hash(final InfoQuery info) {
return hash(Hashing.sha256(), info);
public static EntityCaps2Hash hash(final InfoQuery info) {
return hash(Algorithm.SHA_256, info);
}
public static byte[] hash(HashFunction hashFunction, final InfoQuery info) {
final String algo = algorithm(info);
return hashFunction.hashString(algo, StandardCharsets.UTF_8).asBytes();
public static EntityCaps2Hash hash(final Algorithm algorithm, final InfoQuery info) {
final String result = algorithm(info);
final var hashFunction = toHashFunction(algorithm);
return new EntityCaps2Hash(
algorithm, hashFunction.hashString(result, StandardCharsets.UTF_8).asBytes());
}
private static HashFunction toHashFunction(final Algorithm algorithm) {
switch (algorithm) {
case SHA_1:
return Hashing.sha1();
case SHA_256:
return Hashing.sha256();
case SHA_512:
return Hashing.sha512();
default:
throw new IllegalArgumentException("Unknown hash algorithm");
}
}
private static String asHex(final String message) {
@ -128,4 +146,36 @@ public class EntityCapabilities2 {
EntityCapabilities2::extension)))
+ FILE_SEPARATOR;
}
public static class EntityCaps2Hash extends EntityCapabilities.Hash {
public final Algorithm algorithm;
protected EntityCaps2Hash(final Algorithm algorithm, byte[] hash) {
super(hash);
this.algorithm = algorithm;
}
}
public enum Algorithm {
SHA_1,
SHA_256,
SHA_512;
public static Algorithm tryParse(@Nullable final String name) {
try {
return valueOf(
CaseFormat.LOWER_HYPHEN.to(
CaseFormat.UPPER_UNDERSCORE, Strings.nullToEmpty(name)));
} catch (final IllegalArgumentException e) {
return null;
}
}
@NonNull
@Override
public String toString() {
return CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_HYPHEN, super.toString());
}
}
}

View file

@ -46,8 +46,8 @@ public class DiscoManager extends AbstractManager {
throw new IllegalStateException(
"Node in response did not match node in request");
}
final byte[] caps = EntityCapabilities.hash(infoQuery);
final byte[] caps2 = EntityCapabilities2.hash(infoQuery);
final byte[] caps = EntityCapabilities.hash(infoQuery).hash;
final byte[] caps2 = EntityCapabilities2.hash(infoQuery).hash;
getDatabase()
.discoDao()
.set(getAccount(), entity, node, caps, caps2, infoQuery);

View file

@ -3,7 +3,6 @@ package im.conversations.android.xmpp;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.MatcherAssert.assertThat;
import com.google.common.io.BaseEncoding;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xml.XmlElementReader;
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
@ -34,7 +33,7 @@ public class EntityCapabilitiesTest {
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
assertThat(element, instanceOf(InfoQuery.class));
final InfoQuery info = (InfoQuery) element;
final String var = BaseEncoding.base64().encode(EntityCapabilities.hash(info));
final String var = EntityCapabilities.hash(info).encoded();
Assert.assertEquals("QgayPKawpkPSDYmwT/WM94uAlu0=", var);
}
@ -74,7 +73,7 @@ public class EntityCapabilitiesTest {
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
assertThat(element, instanceOf(InfoQuery.class));
final InfoQuery info = (InfoQuery) element;
final String var = BaseEncoding.base64().encode(EntityCapabilities.hash(info));
final String var = EntityCapabilities.hash(info).encoded();
Assert.assertEquals("q07IKJEyjvHSyhy//CH0CxmKi8w=", var);
}
@ -104,7 +103,7 @@ public class EntityCapabilitiesTest {
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
assertThat(element, instanceOf(InfoQuery.class));
final InfoQuery info = (InfoQuery) element;
final String var = BaseEncoding.base64().encode(EntityCapabilities2.hash(info));
final String var = EntityCapabilities2.hash(info).encoded();
Assert.assertEquals("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=", var);
}
@ -180,7 +179,7 @@ public class EntityCapabilitiesTest {
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
assertThat(element, instanceOf(InfoQuery.class));
final InfoQuery info = (InfoQuery) element;
final String var = BaseEncoding.base64().encode(EntityCapabilities2.hash(info));
final String var = EntityCapabilities2.hash(info).encoded();
Assert.assertEquals("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=", var);
}
}