aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/eu/siacs/conversations/parser
diff options
context:
space:
mode:
authorAndreas Straub <andy@strb.org>2015-07-20 15:13:14 +0200
committerAndreas Straub <andy@strb.org>2015-07-20 15:13:14 +0200
commitaaa8fe8978ac0513a013a9c77efef663f32eda47 (patch)
tree16cc60f67a4fbded2eeeb7ba1001ebb1b1a004a5 /src/main/java/eu/siacs/conversations/parser
parent9c91b9036aa57c610c26b4cdf52ee295eaa0dc95 (diff)
parent19a0ae42d667644ee3400c92c53ad0ad093c52fe (diff)
Merge branch 'CryptoNextBeta' into development
* CryptoNextBeta: (60 commits) Lock TrustKeys if no trusted keys are available Optimize imports Use MD style for key trust toggle switch Fix set/remove OnUpdateBlocklistListener Fix axolotl database migration Remove device list from EditAccount Add clear devices to overflow menu in EditAccount Ask for key trust when sending messages Encrypt files for HTTP upload in encrypted chats Refactor trust key ui and show in account details Send correct body for HTTP files Handle file transmission properly in axolotl Remove unneccessary code Fix trust status for outgoing messages Don't merge messages with different trust statuses Fix copying of axolotl keys to clipboard Add refresh icon to v21 theme Disable Axolotl option if not usable Show trust status of messages' originating session Add key trust toggle to ContactDetailsActivity ...
Diffstat (limited to 'src/main/java/eu/siacs/conversations/parser')
-rw-r--r--src/main/java/eu/siacs/conversations/parser/AbstractParser.java1
-rw-r--r--src/main/java/eu/siacs/conversations/parser/IqParser.java163
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java41
3 files changed, 199 insertions, 6 deletions
diff --git a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
index 24e93db1..18331796 100644
--- a/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/AbstractParser.java
@@ -11,7 +11,6 @@ import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jid.Jid;
-import eu.siacs.conversations.xmpp.stanzas.MessagePacket;
public abstract class AbstractParser {
diff --git a/src/main/java/eu/siacs/conversations/parser/IqParser.java b/src/main/java/eu/siacs/conversations/parser/IqParser.java
index 6039d395..e74cb65c 100644
--- a/src/main/java/eu/siacs/conversations/parser/IqParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/IqParser.java
@@ -1,11 +1,25 @@
package eu.siacs.conversations.parser;
+import android.support.annotation.NonNull;
+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.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import eu.siacs.conversations.Config;
+import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.services.XmppConnectionService;
@@ -71,6 +85,155 @@ 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");
+ }
+
+ @NonNull
+ public Set<Integer> deviceIds(final Element item) {
+ Set<Integer> deviceIds = new HashSet<>();
+ if (item != null) {
+ final Element list = item.findChild("list");
+ if (list != 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, AxolotlService.LOGPREFIX+" : "+"Encountered nvalid <device> 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, AxolotlService.LOGPREFIX+" : "+"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,AxolotlService.LOGPREFIX+" : "+"Invalid identityKey in PEP: "+e.getMessage());
+ }
+ return identityKey;
+ }
+
+ public Map<Integer, ECPublicKey> preKeyPublics(final IqPacket packet) {
+ Map<Integer, ECPublicKey> preKeyRecords = new HashMap<>();
+ Element item = getItem(packet);
+ if (item == null) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Couldn't find <item> in bundle IQ packet: " + packet);
+ return null;
+ }
+ final Element bundleElement = item.findChild("bundle");
+ if(bundleElement == null) {
+ return null;
+ }
+ final Element prekeysElement = bundleElement.findChild("prekeys");
+ if(prekeysElement == null) {
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"Couldn't find <prekeys> in bundle IQ packet: " + packet);
+ return null;
+ }
+ for(Element preKeyPublicElement : prekeysElement.getChildren()) {
+ if(!preKeyPublicElement.getName().equals("preKeyPublic")){
+ Log.d(Config.LOGTAG, AxolotlService.LOGPREFIX+" : "+"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, AxolotlService.LOGPREFIX+" : "+"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<PreKeyBundle> preKeys(final IqPacket preKeys) {
+ List<PreKeyBundle> bundles = new ArrayList<>();
+ Map<Integer, ECPublicKey> 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)) {
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index d46ff195..38e0f98a 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -6,7 +6,11 @@ import android.util.Pair;
import net.java.otr4j.session.Session;
import net.java.otr4j.session.SessionStatus;
+import java.util.Set;
+
import eu.siacs.conversations.Config;
+import eu.siacs.conversations.crypto.axolotl.AxolotlService;
+import eu.siacs.conversations.crypto.axolotl.XmppAxolotlMessage;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
@@ -94,6 +98,20 @@ public class MessageParser extends AbstractParser implements
}
}
+ private Message parseAxolotlChat(Element axolotlMessage, Jid from, String id, Conversation conversation, int status) {
+ Message finishedMessage = null;
+ AxolotlService service = conversation.getAccount().getAxolotlService();
+ XmppAxolotlMessage xmppAxolotlMessage = new XmppAxolotlMessage(from.toBareJid(), axolotlMessage);
+ XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceiving(xmppAxolotlMessage);
+ if(plaintextMessage != null) {
+ finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
+ finishedMessage.setAxolotlFingerprint(plaintextMessage.getFingerprint());
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount())+" Received Message with session fingerprint: "+plaintextMessage.getFingerprint());
+ }
+
+ return finishedMessage;
+ }
+
private class Invite {
Jid jid;
String password;
@@ -170,6 +188,13 @@ public class MessageParser extends AbstractParser implements
mXmppConnectionService.updateConversationUi();
mXmppConnectionService.updateAccountUi();
}
+ } else if (AxolotlService.PEP_DEVICE_LIST.equals(node)) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Received PEP device list update from "+ from + ", processing...");
+ Element item = items.findChild("item");
+ Set<Integer> deviceIds = mXmppConnectionService.getIqParser().deviceIds(item);
+ AxolotlService axolotlService = account.getAxolotlService();
+ axolotlService.registerDevices(from, deviceIds);
+ mXmppConnectionService.updateAccountUi();
}
}
@@ -232,8 +257,9 @@ public class MessageParser extends AbstractParser implements
timestamp = AbstractParser.getTimestamp(packet, System.currentTimeMillis());
}
final String body = packet.getBody();
- final String encrypted = packet.findChildContent("x", "jabber:x:encrypted");
final Element mucUserElement = packet.findChild("x","http://jabber.org/protocol/muc#user");
+ final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
+ final Element axolotlEncrypted = packet.findChild("axolotl_message", AxolotlService.PEP_PREFIX);
int status;
final Jid counterpart;
final Jid to = packet.getTo();
@@ -261,11 +287,11 @@ public class MessageParser extends AbstractParser implements
return;
}
- if (extractChatState(mXmppConnectionService.find(account,from), packet)) {
+ if (extractChatState(mXmppConnectionService.find(account, from), packet)) {
mXmppConnectionService.updateConversationUi();
}
- if ((body != null || encrypted != null) && !isMucStatusMessage) {
+ if ((body != null || pgpEncrypted != null || axolotlEncrypted != null) && !isMucStatusMessage) {
Conversation conversation = mXmppConnectionService.findOrCreateConversation(account, counterpart.toBareJid(), isTypeGroupChat);
if (isTypeGroupChat) {
if (counterpart.getResourcepart().equals(conversation.getMucOptions().getActualNick())) {
@@ -294,8 +320,13 @@ public class MessageParser extends AbstractParser implements
} else {
message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
}
- } else if (encrypted != null) {
- message = new Message(conversation, encrypted, Message.ENCRYPTION_PGP, status);
+ } else if (pgpEncrypted != null) {
+ message = new Message(conversation, pgpEncrypted, Message.ENCRYPTION_PGP, status);
+ } else if (axolotlEncrypted != null) {
+ message = parseAxolotlChat(axolotlEncrypted, from, remoteMsgId, conversation, status);
+ if (message == null) {
+ return;
+ }
} else {
message = new Message(conversation, body, Message.ENCRYPTION_NONE, status);
}