aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java27
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/OnMessageCreatedCallback.java5
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java143
-rw-r--r--src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java8
-rw-r--r--src/main/java/eu/siacs/conversations/parser/MessageParser.java2
5 files changed, 112 insertions, 73 deletions
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
index d1bfe2d4..e4420898 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java
@@ -583,23 +583,15 @@ public class AxolotlService {
if(findSessionsforContact(message.getContact()).isEmpty()) {
return null;
}
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Building axolotl foreign headers...");
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Building axolotl foreign keyElements...");
for (XmppAxolotlSession session : findSessionsforContact(message.getContact())) {
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account)+session.getRemoteAddress().toString());
- //if(!session.isTrusted()) {
- // TODO: handle this properly
- // continue;
- // }
- axolotlMessage.addHeader(session.processSending(axolotlMessage.getInnerKey()));
+ axolotlMessage.addKeyElement(session.processSending(axolotlMessage.getInnerKey()));
}
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Building axolotl own headers...");
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Building axolotl own keyElements...");
for (XmppAxolotlSession session : findOwnSessions()) {
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account)+session.getRemoteAddress().toString());
- // if(!session.isTrusted()) {
- // TODO: handle this properly
- // continue;
- // }
- axolotlMessage.addHeader(session.processSending(axolotlMessage.getInnerKey()));
+ axolotlMessage.addKeyElement(session.processSending(axolotlMessage.getInnerKey()));
}
return axolotlMessage;
@@ -651,7 +643,6 @@ public class AxolotlService {
XmppAxolotlSession session = sessions.get(senderAddress);
if (session == null) {
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Account: "+account.getJid()+" No axolotl session found while parsing received message " + message);
- // TODO: handle this properly
IdentityKey identityKey = axolotlStore.loadSession(senderAddress).getSessionState().getRemoteIdentityKey();
if ( identityKey != null ) {
session = new XmppAxolotlSession(account, axolotlStore, senderAddress, identityKey.getFingerprint().replaceAll("\\s", ""));
@@ -661,12 +652,12 @@ public class AxolotlService {
newSession = true;
}
- for (XmppAxolotlMessage.XmppAxolotlMessageHeader header : message.getHeaders()) {
- if (header.getRecipientDeviceId() == getOwnDeviceId()) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Found axolotl header matching own device ID, processing...");
- byte[] payloadKey = session.processReceiving(header);
+ for (XmppAxolotlMessage.XmppAxolotlKeyElement keyElement : message.getKeyElements()) {
+ if (keyElement.getRecipientDeviceId() == getOwnDeviceId()) {
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Found axolotl keyElement matching own device ID, processing...");
+ byte[] payloadKey = session.processReceiving(keyElement);
if (payloadKey != null) {
- Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Got payload key from axolotl header. Decrypting message...");
+ Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Got payload key from axolotl keyElement. Decrypting message...");
try{
plaintextMessage = message.decrypt(session, payloadKey, session.getFingerprint());
} catch (CryptoFailedException e) {
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/OnMessageCreatedCallback.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/OnMessageCreatedCallback.java
new file mode 100644
index 00000000..3d40a408
--- /dev/null
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/OnMessageCreatedCallback.java
@@ -0,0 +1,5 @@
+package eu.siacs.conversations.crypto.axolotl;
+
+public interface OnMessageCreatedCallback {
+ void run(XmppAxolotlMessage message);
+}
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
index 182c8f12..4725ce8a 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java
@@ -2,6 +2,7 @@ package eu.siacs.conversations.crypto.axolotl;
import android.support.annotation.Nullable;
import android.util.Base64;
+import android.util.Log;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -20,32 +21,46 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
+import eu.siacs.conversations.Config;
import eu.siacs.conversations.xml.Element;
import eu.siacs.conversations.xmpp.jid.Jid;
public class XmppAxolotlMessage {
+ public static final String TAGNAME = "encrypted";
+ public static final String HEADER = "header";
+ public static final String SOURCEID = "sid";
+ public static final String IVTAG = "iv";
+ public static final String PAYLOAD = "payload";
+
+ private static final String KEYTYPE = "AES";
+ private static final String CIPHERMODE = "AES/GCM/NoPadding";
+ private static final String PROVIDER = "BC";
+
private byte[] innerKey;
- private byte[] ciphertext;
- private byte[] iv;
- private final Set<XmppAxolotlMessageHeader> headers;
+ private byte[] ciphertext = null;
+ private byte[] iv = null;
+ private final Set<XmppAxolotlKeyElement> keyElements;
private final Jid from;
private final int sourceDeviceId;
- public static class XmppAxolotlMessageHeader {
+ public static class XmppAxolotlKeyElement {
+ public static final String TAGNAME = "key";
+ public static final String REMOTEID = "rid";
+
private final int recipientDeviceId;
private final byte[] content;
- public XmppAxolotlMessageHeader(int deviceId, byte[] content) {
+ public XmppAxolotlKeyElement(int deviceId, byte[] content) {
this.recipientDeviceId = deviceId;
this.content = content;
}
- public XmppAxolotlMessageHeader(Element header) {
- if("header".equals(header.getName())) {
- this.recipientDeviceId = Integer.parseInt(header.getAttribute("rid"));
- this.content = Base64.decode(header.getContent(),Base64.DEFAULT);
+ public XmppAxolotlKeyElement(Element keyElement) {
+ if(TAGNAME.equals(keyElement.getName())) {
+ this.recipientDeviceId = Integer.parseInt(keyElement.getAttribute(REMOTEID));
+ this.content = Base64.decode(keyElement.getContent(),Base64.DEFAULT);
} else {
- throw new IllegalArgumentException("Argument not a <header> Element!");
+ throw new IllegalArgumentException("Argument not a <"+TAGNAME+"> Element!");
}
}
@@ -58,11 +73,10 @@ public class XmppAxolotlMessage {
}
public Element toXml() {
- Element headerElement = new Element("header");
- // TODO: generate XML
- headerElement.setAttribute("rid", getRecipientDeviceId());
- headerElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT));
- return headerElement;
+ Element keyElement = new Element(TAGNAME);
+ keyElement.setAttribute(REMOTEID, getRecipientDeviceId());
+ keyElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT));
+ return keyElement;
}
}
@@ -90,42 +104,69 @@ public class XmppAxolotlMessage {
}
}
- public XmppAxolotlMessage(Jid from, Element axolotlMessage) {
+ public XmppAxolotlMessage(Jid from, Element axolotlMessage) throws IllegalArgumentException {
this.from = from;
- this.sourceDeviceId = Integer.parseInt(axolotlMessage.getAttribute("id"));
- this.headers = new HashSet<>();
- for(Element child:axolotlMessage.getChildren()) {
- switch(child.getName()) {
- case "header":
- headers.add(new XmppAxolotlMessageHeader(child));
+ Element header = axolotlMessage.findChild(HEADER);
+ this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
+ this.keyElements = new HashSet<>();
+ for(Element keyElement:header.getChildren()) {
+ switch(keyElement.getName()) {
+ case XmppAxolotlKeyElement.TAGNAME:
+ keyElements.add(new XmppAxolotlKeyElement(keyElement));
break;
- case "message":
- iv = Base64.decode(child.getAttribute("iv"),Base64.DEFAULT);
- ciphertext = Base64.decode(child.getContent(),Base64.DEFAULT);
+ case IVTAG:
+ if ( this.iv != null) {
+ throw new IllegalArgumentException("Duplicate iv entry");
+ }
+ iv = Base64.decode(keyElement.getContent(),Base64.DEFAULT);
break;
default:
+ Log.w(Config.LOGTAG, "Unexpected element in header: "+ keyElement.toString());
break;
}
}
+ Element payloadElement = axolotlMessage.findChild(PAYLOAD);
+ if ( payloadElement != null ) {
+ ciphertext = Base64.decode(payloadElement.getContent(), Base64.DEFAULT);
+ }
}
- public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) throws CryptoFailedException{
+ public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
this.from = from;
this.sourceDeviceId = sourceDeviceId;
- this.headers = new HashSet<>();
+ this.keyElements = new HashSet<>();
+ this.iv = generateIv();
+ this.innerKey = generateKey();
+ }
+
+ public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) throws CryptoFailedException{
+ this(from, sourceDeviceId);
this.encrypt(plaintext);
}
- private void encrypt(String plaintext) throws CryptoFailedException {
+ private static byte[] generateKey() {
try {
- KeyGenerator generator = KeyGenerator.getInstance("AES");
+ KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
generator.init(128);
- SecretKey secretKey = generator.generateKey();
- SecureRandom random = new SecureRandom();
- this.iv = new byte[16];
- random.nextBytes(iv);
+ return generator.generateKey().getEncoded();
+ } catch (NoSuchAlgorithmException e) {
+ Log.e(Config.LOGTAG, e.getMessage());
+ return null;
+ }
+ }
+
+ private static byte[] generateIv() {
+ SecureRandom random = new SecureRandom();
+ byte[] iv = new byte[16];
+ random.nextBytes(iv);
+ return iv;
+ }
+
+ private void encrypt(String plaintext) throws CryptoFailedException {
+ try {
+ SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
+ Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
this.innerKey = secretKey.getEncoded();
this.ciphertext = cipher.doFinal(plaintext.getBytes());
@@ -148,13 +189,13 @@ public class XmppAxolotlMessage {
return ciphertext;
}
- public Set<XmppAxolotlMessageHeader> getHeaders() {
- return headers;
+ public Set<XmppAxolotlKeyElement> getKeyElements() {
+ return keyElements;
}
- public void addHeader(@Nullable XmppAxolotlMessageHeader header) {
- if (header != null) {
- headers.add(header);
+ public void addKeyElement(@Nullable XmppAxolotlKeyElement keyElement) {
+ if (keyElement != null) {
+ keyElements.add(keyElement);
}
}
@@ -167,16 +208,18 @@ public class XmppAxolotlMessage {
}
public Element toXml() {
- // TODO: generate outer XML, add in header XML
- Element message= new Element("axolotl_message", AxolotlService.PEP_PREFIX);
- message.setAttribute("id", sourceDeviceId);
- for(XmppAxolotlMessageHeader header: headers) {
- message.addChild(header.toXml());
+ Element encryptionElement= new Element(TAGNAME, AxolotlService.PEP_PREFIX);
+ Element headerElement = encryptionElement.addChild(HEADER);
+ headerElement.setAttribute(SOURCEID, sourceDeviceId);
+ for(XmppAxolotlKeyElement header: keyElements) {
+ headerElement.addChild(header.toXml());
+ }
+ headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.DEFAULT));
+ if ( ciphertext != null ) {
+ Element payload = encryptionElement.addChild(PAYLOAD);
+ payload.setContent(Base64.encodeToString(ciphertext, Base64.DEFAULT));
}
- Element payload = message.addChild("message");
- payload.setAttribute("iv",Base64.encodeToString(iv, Base64.DEFAULT));
- payload.setContent(Base64.encodeToString(ciphertext,Base64.DEFAULT));
- return message;
+ return encryptionElement;
}
@@ -184,8 +227,8 @@ public class XmppAxolotlMessage {
XmppAxolotlPlaintextMessage plaintextMessage = null;
try {
- Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
- SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
+ Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
index d58b2dd8..dc4c6777 100644
--- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
+++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlSession.java
@@ -69,7 +69,7 @@ public class XmppAxolotlSession {
}
@Nullable
- public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) {
+ public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlKeyElement incomingHeader) {
byte[] plaintext = null;
SQLiteAxolotlStore.Trust trust = getTrust();
switch (trust) {
@@ -117,12 +117,12 @@ public class XmppAxolotlSession {
}
@Nullable
- public XmppAxolotlMessage.XmppAxolotlMessageHeader processSending(@NonNull byte[] outgoingMessage) {
+ public XmppAxolotlMessage.XmppAxolotlKeyElement processSending(@NonNull byte[] outgoingMessage) {
SQLiteAxolotlStore.Trust trust = getTrust();
if (trust == SQLiteAxolotlStore.Trust.TRUSTED) {
CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
- XmppAxolotlMessage.XmppAxolotlMessageHeader header =
- new XmppAxolotlMessage.XmppAxolotlMessageHeader(remoteAddress.getDeviceId(),
+ XmppAxolotlMessage.XmppAxolotlKeyElement header =
+ new XmppAxolotlMessage.XmppAxolotlKeyElement(remoteAddress.getDeviceId(),
ciphertextMessage.serialize());
return header;
} else {
diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
index 4bee34a9..47c320f3 100644
--- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java
+++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java
@@ -272,7 +272,7 @@ public class MessageParser extends AbstractParser implements
final String body = packet.getBody();
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);
+ final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.TAGNAME, AxolotlService.PEP_PREFIX);
int status;
final Jid counterpart;
final Jid to = packet.getTo();