use dedicated hash object instead of byte[] for caps
this way we can store the algo alongside the object
This commit is contained in:
parent
6458c6e9f9
commit
a2b21d97eb
|
@ -5,6 +5,7 @@ import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.ComparisonChain;
|
import com.google.common.collect.ComparisonChain;
|
||||||
import com.google.common.collect.Ordering;
|
import com.google.common.collect.Ordering;
|
||||||
import com.google.common.hash.Hashing;
|
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.Data;
|
||||||
import im.conversations.android.xmpp.model.data.Field;
|
import im.conversations.android.xmpp.model.data.Field;
|
||||||
import im.conversations.android.xmpp.model.disco.info.Feature;
|
import im.conversations.android.xmpp.model.disco.info.Feature;
|
||||||
|
@ -15,7 +16,7 @@ import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public final class EntityCapabilities {
|
public final class EntityCapabilities {
|
||||||
public static byte[] hash(final InfoQuery info) {
|
public static EntityCapsHash hash(final InfoQuery info) {
|
||||||
final StringBuilder s = new StringBuilder();
|
final StringBuilder s = new StringBuilder();
|
||||||
final List<Identity> orderedIdentities =
|
final List<Identity> orderedIdentities =
|
||||||
Ordering.from(
|
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) {
|
private static String clean(String s) {
|
||||||
|
@ -86,4 +88,23 @@ public final class EntityCapabilities {
|
||||||
private static String blankNull(String s) {
|
private static String blankNull(String s) {
|
||||||
return s == null ? "" : clean(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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
package im.conversations.android.xmpp;
|
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.Joiner;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
|
@ -25,13 +28,28 @@ public class EntityCapabilities2 {
|
||||||
|
|
||||||
private static final char FILE_SEPARATOR = 0x1c;
|
private static final char FILE_SEPARATOR = 0x1c;
|
||||||
|
|
||||||
public static byte[] hash(final InfoQuery info) {
|
public static EntityCaps2Hash hash(final InfoQuery info) {
|
||||||
return hash(Hashing.sha256(), info);
|
return hash(Algorithm.SHA_256, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] hash(HashFunction hashFunction, final InfoQuery info) {
|
public static EntityCaps2Hash hash(final Algorithm algorithm, final InfoQuery info) {
|
||||||
final String algo = algorithm(info);
|
final String result = algorithm(info);
|
||||||
return hashFunction.hashString(algo, StandardCharsets.UTF_8).asBytes();
|
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) {
|
private static String asHex(final String message) {
|
||||||
|
@ -128,4 +146,36 @@ public class EntityCapabilities2 {
|
||||||
EntityCapabilities2::extension)))
|
EntityCapabilities2::extension)))
|
||||||
+ FILE_SEPARATOR;
|
+ 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,8 +46,8 @@ public class DiscoManager extends AbstractManager {
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Node in response did not match node in request");
|
"Node in response did not match node in request");
|
||||||
}
|
}
|
||||||
final byte[] caps = EntityCapabilities.hash(infoQuery);
|
final byte[] caps = EntityCapabilities.hash(infoQuery).hash;
|
||||||
final byte[] caps2 = EntityCapabilities2.hash(infoQuery);
|
final byte[] caps2 = EntityCapabilities2.hash(infoQuery).hash;
|
||||||
getDatabase()
|
getDatabase()
|
||||||
.discoDao()
|
.discoDao()
|
||||||
.set(getAccount(), entity, node, caps, caps2, infoQuery);
|
.set(getAccount(), entity, node, caps, caps2, infoQuery);
|
||||||
|
|
|
@ -3,7 +3,6 @@ package im.conversations.android.xmpp;
|
||||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
|
|
||||||
import com.google.common.io.BaseEncoding;
|
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xml.XmlElementReader;
|
import eu.siacs.conversations.xml.XmlElementReader;
|
||||||
import im.conversations.android.xmpp.model.disco.info.InfoQuery;
|
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));
|
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
||||||
assertThat(element, instanceOf(InfoQuery.class));
|
assertThat(element, instanceOf(InfoQuery.class));
|
||||||
final InfoQuery info = (InfoQuery) element;
|
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);
|
Assert.assertEquals("QgayPKawpkPSDYmwT/WM94uAlu0=", var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,7 +73,7 @@ public class EntityCapabilitiesTest {
|
||||||
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
||||||
assertThat(element, instanceOf(InfoQuery.class));
|
assertThat(element, instanceOf(InfoQuery.class));
|
||||||
final InfoQuery info = (InfoQuery) element;
|
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);
|
Assert.assertEquals("q07IKJEyjvHSyhy//CH0CxmKi8w=", var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,7 +103,7 @@ public class EntityCapabilitiesTest {
|
||||||
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
||||||
assertThat(element, instanceOf(InfoQuery.class));
|
assertThat(element, instanceOf(InfoQuery.class));
|
||||||
final InfoQuery info = (InfoQuery) element;
|
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);
|
Assert.assertEquals("kzBZbkqJ3ADrj7v08reD1qcWUwNGHaidNUgD7nHpiw8=", var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +179,7 @@ public class EntityCapabilitiesTest {
|
||||||
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
final Element element = XmlElementReader.read(xml.getBytes(StandardCharsets.UTF_8));
|
||||||
assertThat(element, instanceOf(InfoQuery.class));
|
assertThat(element, instanceOf(InfoQuery.class));
|
||||||
final InfoQuery info = (InfoQuery) element;
|
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);
|
Assert.assertEquals("u79ZroNJbdSWhdSp311mddz44oHHPsEBntQ5b1jqBSY=", var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue