From 77619b55e47aafbe6b5a530b33ce8b6a77a615dd Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Thu, 25 Jun 2015 16:58:24 +0200 Subject: Added PEP and message protocol layers Can now fetch/retrieve from PEP, as well as encode/decode messages --- .../eu/siacs/conversations/parser/IqParser.java | 158 ++++++++++++++++++++- 1 file changed, 157 insertions(+), 1 deletion(-) (limited to 'src/main/java/eu/siacs/conversations/parser/IqParser.java') diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java index 6039d3956..00f968858 100644 --- a/src/main/java/eu/siacs/conversations/parser/IqParser.java +++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java @@ -1,9 +1,19 @@ package eu.siacs.conversations.parser; +import android.util.Base64; import android.util.Log; +import org.whispersystems.libaxolotl.IdentityKey; +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.state.PreKeyBundle; + import java.util.ArrayList; import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import eu.siacs.conversations.Config; import eu.siacs.conversations.entities.Account; @@ -60,7 +70,7 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { public String avatarData(final IqPacket packet) { final Element pubsub = packet.findChild("pubsub", - "http://jabber.org/protocol/pubsub"); + "http://jabber.org/protocol/pubsub"); if (pubsub == null) { return null; } @@ -71,6 +81,152 @@ public class IqParser extends AbstractParser implements OnIqPacketReceived { return super.avatarData(items); } + public Element getItem(final IqPacket packet) { + final Element pubsub = packet.findChild("pubsub", + "http://jabber.org/protocol/pubsub"); + if (pubsub == null) { + return null; + } + final Element items = pubsub.findChild("items"); + if (items == null) { + return null; + } + return items.findChild("item"); + } + + public List deviceIds(final Element item) { + List deviceIds = new ArrayList<>(); + if (item == null) { + return null; + } + final Element list = item.findChild("list"); + if(list == null) { + return null; + } + for(Element device : list.getChildren()) { + if(!device.getName().equals("device")) { + continue; + } + try { + Integer id = Integer.valueOf(device.getAttribute("id")); + deviceIds.add(id); + } catch (NumberFormatException e) { + Log.e(Config.LOGTAG, "Encountered nvalid node in PEP:" + device.toString() + + ", skipping..."); + continue; + } + } + return deviceIds; + } + + public Integer signedPreKeyId(final Element bundle) { + final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic"); + if(signedPreKeyPublic == null) { + return null; + } + return Integer.valueOf(signedPreKeyPublic.getAttribute("signedPreKeyId")); + } + + public ECPublicKey signedPreKeyPublic(final Element bundle) { + ECPublicKey publicKey = null; + final Element signedPreKeyPublic = bundle.findChild("signedPreKeyPublic"); + if(signedPreKeyPublic == null) { + return null; + } + try { + publicKey = Curve.decodePoint(Base64.decode(signedPreKeyPublic.getContent(),Base64.DEFAULT), 0); + } catch (InvalidKeyException e) { + Log.e(Config.LOGTAG, "Invalid signedPreKeyPublic in PEP: " + e.getMessage()); + } + return publicKey; + } + + public byte[] signedPreKeySignature(final Element bundle) { + final Element signedPreKeySignature = bundle.findChild("signedPreKeySignature"); + if(signedPreKeySignature == null) { + return null; + } + return Base64.decode(signedPreKeySignature.getContent(),Base64.DEFAULT); + } + + public IdentityKey identityKey(final Element bundle) { + IdentityKey identityKey = null; + final Element identityKeyElement = bundle.findChild("identityKey"); + if(identityKeyElement == null) { + return null; + } + try { + identityKey = new IdentityKey(Base64.decode(identityKeyElement.getContent(), Base64.DEFAULT), 0); + } catch (InvalidKeyException e) { + Log.e(Config.LOGTAG,"Invalid identityKey in PEP: "+e.getMessage()); + } + return identityKey; + } + + public Map preKeyPublics(final IqPacket packet) { + Map preKeyRecords = new HashMap<>(); + Element prekeysItem = getItem(packet); + if (prekeysItem == null) { + Log.d(Config.LOGTAG, "Couldn't find in preKeyPublic IQ packet: " + packet); + return null; + } + final Element prekeysElement = prekeysItem.findChild("prekeys"); + if(prekeysElement == null) { + Log.d(Config.LOGTAG, "Couldn't find in preKeyPublic IQ packet: " + packet); + return null; + } + for(Element preKeyPublicElement : prekeysElement.getChildren()) { + if(!preKeyPublicElement.getName().equals("preKeyPublic")){ + Log.d(Config.LOGTAG, "Encountered unexpected tag in prekeys list: " + preKeyPublicElement); + continue; + } + Integer preKeyId = Integer.valueOf(preKeyPublicElement.getAttribute("preKeyId")); + try { + ECPublicKey preKeyPublic = Curve.decodePoint(Base64.decode(preKeyPublicElement.getContent(), Base64.DEFAULT), 0); + preKeyRecords.put(preKeyId, preKeyPublic); + } catch (InvalidKeyException e) { + Log.e(Config.LOGTAG, "Invalid preKeyPublic (ID="+preKeyId+") in PEP: "+ e.getMessage()+", skipping..."); + continue; + } + } + return preKeyRecords; + } + + public PreKeyBundle bundle(final IqPacket bundle) { + Element bundleItem = getItem(bundle); + if(bundleItem == null) { + return null; + } + final Element bundleElement = bundleItem.findChild("bundle"); + if(bundleElement == null) { + return null; + } + ECPublicKey signedPreKeyPublic = signedPreKeyPublic(bundleElement); + Integer signedPreKeyId = signedPreKeyId(bundleElement); + byte[] signedPreKeySignature = signedPreKeySignature(bundleElement); + IdentityKey identityKey = identityKey(bundleElement); + if(signedPreKeyPublic == null || identityKey == null) { + return null; + } + + return new PreKeyBundle(0, 0, 0, null, + signedPreKeyId, signedPreKeyPublic, signedPreKeySignature, identityKey); + } + + public List preKeys(final IqPacket preKeys) { + List bundles = new ArrayList<>(); + Map preKeyPublics = preKeyPublics(preKeys); + if ( preKeyPublics != null) { + for (Integer preKeyId : preKeyPublics.keySet()) { + ECPublicKey preKeyPublic = preKeyPublics.get(preKeyId); + bundles.add(new PreKeyBundle(0, 0, preKeyId, preKeyPublic, + 0, null, null, null)); + } + } + + return bundles; + } + @Override public void onIqPacketReceived(final Account account, final IqPacket packet) { if (packet.hasChild("query", Xmlns.ROSTER) && packet.fromServer(account)) { -- cgit v1.2.3