code cleanup in bundle parsing
also switch to guavas base64 parser to avoid potential ROM bugs
This commit is contained in:
parent
4f8715a349
commit
5a5f887229
|
@ -2,11 +2,13 @@ package eu.siacs.conversations.parser;
|
||||||
|
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Base64;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
|
import com.google.common.io.BaseEncoding;
|
||||||
|
|
||||||
import org.whispersystems.libsignal.IdentityKey;
|
import org.whispersystems.libsignal.IdentityKey;
|
||||||
|
import org.whispersystems.libsignal.InvalidKeyException;
|
||||||
import org.whispersystems.libsignal.ecc.Curve;
|
import org.whispersystems.libsignal.ecc.Curve;
|
||||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||||
|
@ -27,12 +29,10 @@ import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
|
||||||
import eu.siacs.conversations.entities.Account;
|
import eu.siacs.conversations.entities.Account;
|
||||||
import eu.siacs.conversations.entities.Contact;
|
import eu.siacs.conversations.entities.Contact;
|
||||||
import eu.siacs.conversations.entities.Conversation;
|
|
||||||
import eu.siacs.conversations.entities.Room;
|
import eu.siacs.conversations.entities.Room;
|
||||||
import eu.siacs.conversations.services.ChannelDiscoveryService;
|
|
||||||
import eu.siacs.conversations.services.XmppConnectionService;
|
import eu.siacs.conversations.services.XmppConnectionService;
|
||||||
import eu.siacs.conversations.xml.Namespace;
|
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
|
import eu.siacs.conversations.xml.Namespace;
|
||||||
import eu.siacs.conversations.xmpp.InvalidJid;
|
import eu.siacs.conversations.xmpp.InvalidJid;
|
||||||
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
|
import eu.siacs.conversations.xmpp.OnIqPacketReceived;
|
||||||
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
|
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
|
||||||
|
@ -46,6 +46,56 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
super(service);
|
super(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<Jid> items(IqPacket packet) {
|
||||||
|
ArrayList<Jid> items = new ArrayList<>();
|
||||||
|
final Element query = packet.findChild("query", Namespace.DISCO_ITEMS);
|
||||||
|
if (query == null) {
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
for (Element child : query.getChildren()) {
|
||||||
|
if ("item".equals(child.getName())) {
|
||||||
|
Jid jid = child.getAttributeAsJid("jid");
|
||||||
|
if (jid != null) {
|
||||||
|
items.add(jid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Room parseRoom(IqPacket packet) {
|
||||||
|
final Element query = packet.findChild("query", Namespace.DISCO_INFO);
|
||||||
|
if (query == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Element x = query.findChild("x");
|
||||||
|
if (x == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final Element identity = query.findChild("identity");
|
||||||
|
Data data = Data.parse(x);
|
||||||
|
String address = packet.getFrom().toEscapedString();
|
||||||
|
String name = identity == null ? null : identity.getAttribute("name");
|
||||||
|
String roomName = data.getValue("muc#roomconfig_roomname");
|
||||||
|
String description = data.getValue("muc#roominfo_description");
|
||||||
|
String language = data.getValue("muc#roominfo_lang");
|
||||||
|
String occupants = data.getValue("muc#roominfo_occupants");
|
||||||
|
int nusers;
|
||||||
|
try {
|
||||||
|
nusers = occupants == null ? 0 : Integer.parseInt(occupants);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
nusers = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Room(
|
||||||
|
address,
|
||||||
|
TextUtils.isEmpty(roomName) ? name : roomName,
|
||||||
|
description,
|
||||||
|
language,
|
||||||
|
nusers
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private void rosterItems(final Account account, final Element query) {
|
private void rosterItems(final Account account, final Element query) {
|
||||||
final String version = query.getAttribute("ver");
|
final String version = query.getAttribute("ver");
|
||||||
if (version != null) {
|
if (version != null) {
|
||||||
|
@ -130,7 +180,6 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
deviceIds.add(id);
|
deviceIds.add(id);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Encountered invalid <device> node in PEP (" + e.getMessage() + "):" + device.toString() + ", skipping...");
|
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Encountered invalid <device> node in PEP (" + e.getMessage() + "):" + device.toString() + ", skipping...");
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,7 +187,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
return deviceIds;
|
return deviceIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer signedPreKeyId(final Element bundle) {
|
private Integer signedPreKeyId(final Element bundle) {
|
||||||
final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
|
final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
|
||||||
if (signedPreKeyPublic == null) {
|
if (signedPreKeyPublic == null) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -150,45 +199,44 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ECPublicKey signedPreKeyPublic(final Element bundle) {
|
private ECPublicKey signedPreKeyPublic(final Element bundle) {
|
||||||
ECPublicKey publicKey = null;
|
ECPublicKey publicKey = null;
|
||||||
final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic");
|
final String signedPreKeyPublic = bundle.findChildContent("signedPreKeyPublic");
|
||||||
if (signedPreKeyPublic == null) {
|
if (signedPreKeyPublic == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
publicKey = Curve.decodePoint(Base64.decode(signedPreKeyPublic.getContent(), Base64.DEFAULT), 0);
|
publicKey = Curve.decodePoint(BaseEncoding.base64().decode(signedPreKeyPublic), 0);
|
||||||
} catch (Throwable e) {
|
} catch (final IllegalArgumentException | InvalidKeyException e) {
|
||||||
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid signedPreKeyPublic in PEP: " + e.getMessage());
|
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid signedPreKeyPublic in PEP: " + e.getMessage());
|
||||||
}
|
}
|
||||||
return publicKey;
|
return publicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] signedPreKeySignature(final Element bundle) {
|
private byte[] signedPreKeySignature(final Element bundle) {
|
||||||
final Element signedPreKeySignature = bundle.findChild("signedPreKeySignature");
|
final String signedPreKeySignature = bundle.findChildContent("signedPreKeySignature");
|
||||||
if (signedPreKeySignature == null) {
|
if (signedPreKeySignature == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
return Base64.decode(signedPreKeySignature.getContent(), Base64.DEFAULT);
|
return BaseEncoding.base64().decode(signedPreKeySignature);
|
||||||
} catch (Throwable e) {
|
} catch (final IllegalArgumentException e) {
|
||||||
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : Invalid base64 in signedPreKeySignature");
|
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : Invalid base64 in signedPreKeySignature");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public IdentityKey identityKey(final Element bundle) {
|
private IdentityKey identityKey(final Element bundle) {
|
||||||
IdentityKey identityKey = null;
|
final String identityKey = bundle.findChildContent("identityKey");
|
||||||
final Element identityKeyElement = bundle.findChild("identityKey");
|
if (identityKey == null) {
|
||||||
if (identityKeyElement == null) {
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
identityKey = new IdentityKey(Base64.decode(identityKeyElement.getContent(), Base64.DEFAULT), 0);
|
return new IdentityKey(BaseEncoding.base64().decode(identityKey), 0);
|
||||||
} catch (Throwable e) {
|
} catch (final IllegalArgumentException | InvalidKeyException e) {
|
||||||
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid identityKey in PEP: " + e.getMessage());
|
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "Invalid identityKey in PEP: " + e.getMessage());
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
return identityKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Integer, ECPublicKey> preKeyPublics(final IqPacket packet) {
|
public Map<Integer, ECPublicKey> preKeyPublics(final IqPacket packet) {
|
||||||
|
@ -215,7 +263,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
Integer preKeyId = null;
|
Integer preKeyId = null;
|
||||||
try {
|
try {
|
||||||
preKeyId = Integer.valueOf(preKeyPublicElement.getAttribute("preKeyId"));
|
preKeyId = Integer.valueOf(preKeyPublicElement.getAttribute("preKeyId"));
|
||||||
final ECPublicKey preKeyPublic = Curve.decodePoint(Base64.decode(preKeyPublicElement.getContent(), Base64.DEFAULT), 0);
|
final ECPublicKey preKeyPublic = Curve.decodePoint(BaseEncoding.base64().decode(preKeyPublicElement.getContent()), 0);
|
||||||
preKeyRecords.put(preKeyId, preKeyPublic);
|
preKeyRecords.put(preKeyId, preKeyPublic);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "could not parse preKeyId from preKey " + preKeyPublicElement.toString());
|
Log.e(Config.LOGTAG, AxolotlService.LOGPREFIX + " : " + "could not parse preKeyId from preKey " + preKeyPublicElement.toString());
|
||||||
|
@ -230,18 +278,22 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
Element item = getItem(packet);
|
Element item = getItem(packet);
|
||||||
Element verification = item != null ? item.findChild("verification", AxolotlService.PEP_PREFIX) : null;
|
Element verification = item != null ? item.findChild("verification", AxolotlService.PEP_PREFIX) : null;
|
||||||
Element chain = verification != null ? verification.findChild("chain") : null;
|
Element chain = verification != null ? verification.findChild("chain") : null;
|
||||||
Element signature = verification != null ? verification.findChild("signature") : null;
|
String signature = verification != null ? verification.findChildContent("signature") : null;
|
||||||
if (chain != null && signature != null) {
|
if (chain != null && signature != null) {
|
||||||
List<Element> certElements = chain.getChildren();
|
List<Element> certElements = chain.getChildren();
|
||||||
X509Certificate[] certificates = new X509Certificate[certElements.size()];
|
X509Certificate[] certificates = new X509Certificate[certElements.size()];
|
||||||
try {
|
try {
|
||||||
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (Element cert : certElements) {
|
for (final Element certElement : certElements) {
|
||||||
certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(Base64.decode(cert.getContent(), Base64.DEFAULT)));
|
final String cert = certElement.getContent();
|
||||||
|
if (cert == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
certificates[i] = (X509Certificate) certificateFactory.generateCertificate(new ByteArrayInputStream(BaseEncoding.base64().decode(cert)));
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
return new Pair<>(certificates, Base64.decode(signature.getContent(), Base64.DEFAULT));
|
return new Pair<>(certificates, BaseEncoding.base64().decode(signature));
|
||||||
} catch (CertificateException e) {
|
} catch (CertificateException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -251,7 +303,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
}
|
}
|
||||||
|
|
||||||
public PreKeyBundle bundle(final IqPacket bundle) {
|
public PreKeyBundle bundle(final IqPacket bundle) {
|
||||||
Element bundleItem = getItem(bundle);
|
final Element bundleItem = getItem(bundle);
|
||||||
if (bundleItem == null) {
|
if (bundleItem == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -259,14 +311,17 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
if (bundleElement == null) {
|
if (bundleElement == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
|
final ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement);
|
||||||
Integer signedPreKeyId = signedPreKeyId(bundleElement);
|
final Integer signedPreKeyId = signedPreKeyId(bundleElement);
|
||||||
byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
|
final byte[] signedPreKeySignature = signedPreKeySignature(bundleElement);
|
||||||
IdentityKey identityKey = identityKey(bundleElement);
|
final IdentityKey identityKey = identityKey(bundleElement);
|
||||||
if (signedPreKeyId == null || signedPreKeyPublic == null || identityKey == null) {
|
if (signedPreKeyId == null
|
||||||
|
|| signedPreKeyPublic == null
|
||||||
|
|| identityKey == null
|
||||||
|
|| signedPreKeySignature == null
|
||||||
|
|| signedPreKeySignature.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new PreKeyBundle(0, 0, 0, null,
|
return new PreKeyBundle(0, 0, 0, null,
|
||||||
signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
|
signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey);
|
||||||
}
|
}
|
||||||
|
@ -398,55 +453,4 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static List<Jid> items(IqPacket packet) {
|
|
||||||
ArrayList<Jid> items = new ArrayList<>();
|
|
||||||
final Element query = packet.findChild("query", Namespace.DISCO_ITEMS);
|
|
||||||
if (query == null) {
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
for(Element child : query.getChildren()) {
|
|
||||||
if ("item".equals(child.getName())) {
|
|
||||||
Jid jid = child.getAttributeAsJid("jid");
|
|
||||||
if (jid != null) {
|
|
||||||
items.add(jid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Room parseRoom(IqPacket packet) {
|
|
||||||
final Element query = packet.findChild("query", Namespace.DISCO_INFO);
|
|
||||||
if(query == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final Element x = query.findChild("x");
|
|
||||||
if (x == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
final Element identity = query.findChild("identity");
|
|
||||||
Data data = Data.parse(x);
|
|
||||||
String address = packet.getFrom().toEscapedString();
|
|
||||||
String name = identity == null ? null : identity.getAttribute("name");
|
|
||||||
String roomName = data.getValue("muc#roomconfig_roomname");;
|
|
||||||
String description = data.getValue("muc#roominfo_description");
|
|
||||||
String language = data.getValue("muc#roominfo_lang");
|
|
||||||
String occupants = data.getValue("muc#roominfo_occupants");
|
|
||||||
int nusers;
|
|
||||||
try {
|
|
||||||
nusers = occupants == null ? 0 : Integer.parseInt(occupants);
|
|
||||||
} catch (NumberFormatException e) {
|
|
||||||
nusers = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Room(
|
|
||||||
address,
|
|
||||||
TextUtils.isEmpty(roomName) ? name : roomName,
|
|
||||||
description,
|
|
||||||
language,
|
|
||||||
nusers
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue