From b8048a5538293b3855c2b2eae55d35645e614f11 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Fri, 29 May 2015 11:17:26 +0200 Subject: CryptoNext persistance layer mockup Initial sketch of the peripheral storage infrastructure for the new axolotl-based encryption scheme. --- .../eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java new file mode 100644 index 00000000..b11670e4 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -0,0 +1,4 @@ +package eu.siacs.conversations.crypto.axolotl; + +public class XmppAxolotlMessage { +} -- cgit v1.2.3 From f73aa1a2006beb741bc39026bfd10e6166d7951a Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Thu, 25 Jun 2015 16:56:34 +0200 Subject: Reworked axolotl protocol layer Numerous fixes --- .../crypto/axolotl/XmppAxolotlMessage.java | 180 +++++++++++++++++++++ 1 file changed, 180 insertions(+) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 b11670e4..4b87fc5c 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -1,4 +1,184 @@ package eu.siacs.conversations.crypto.axolotl; +import android.util.Base64; + +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.security.InvalidKeyException; +import java.util.HashSet; +import java.util.Set; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import eu.siacs.conversations.entities.Contact; +import eu.siacs.conversations.xml.Element; + public class XmppAxolotlMessage { + private byte[] innerKey; + private byte[] ciphertext; + private byte[] iv; + private final Set headers; + private final Contact contact; + private final int sourceDeviceId; + + public static class XmppAxolotlMessageHeader { + private final int recipientDeviceId; + private final byte[] content; + + public XmppAxolotlMessageHeader(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); + } else { + throw new IllegalArgumentException("Argument not a
Element!"); + } + } + + public int getRecipientDeviceId() { + return recipientDeviceId; + } + + public byte[] getContents() { + return content; + } + + public Element toXml() { + Element headerElement = new Element("header"); + // TODO: generate XML + headerElement.setAttribute("rid", getRecipientDeviceId()); + headerElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT)); + return headerElement; + } + } + + public static class XmppAxolotlPlaintextMessage { + private final AxolotlService.XmppAxolotlSession session; + private final String plaintext; + + public XmppAxolotlPlaintextMessage(AxolotlService.XmppAxolotlSession session, String plaintext) { + this.session = session; + this.plaintext = plaintext; + } + + public String getPlaintext() { + return plaintext; + } + } + + public XmppAxolotlMessage(Contact contact, Element axolotlMessage) { + this.contact = contact; + 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)); + break; + case "message": + iv = Base64.decode(child.getAttribute("iv"),Base64.DEFAULT); + ciphertext = Base64.decode(child.getContent(),Base64.DEFAULT); + break; + default: + break; + } + } + } + + public XmppAxolotlMessage(Contact contact, int sourceDeviceId, String plaintext) { + this.contact = contact; + this.sourceDeviceId = sourceDeviceId; + this.headers = new HashSet<>(); + this.encrypt(plaintext); + } + + private void encrypt(String plaintext) { + try { + KeyGenerator generator = KeyGenerator.getInstance("AES"); + generator.init(128); + SecretKey secretKey = generator.generateKey(); + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + this.innerKey = secretKey.getEncoded(); + this.iv = cipher.getIV(); + this.ciphertext = cipher.doFinal(plaintext.getBytes()); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | IllegalBlockSizeException | BadPaddingException e) { + + } + } + + public Contact getContact() { + return this.contact; + } + + public int getSenderDeviceId() { + return sourceDeviceId; + } + + public byte[] getCiphertext() { + return ciphertext; + } + + public Set getHeaders() { + return headers; + } + + public void addHeader(XmppAxolotlMessageHeader header) { + headers.add(header); + } + + public byte[] getInnerKey(){ + return innerKey; + } + + public byte[] getIV() { + return this.iv; + } + + 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 payload = message.addChild("message"); + payload.setAttribute("iv",Base64.encodeToString(iv, Base64.DEFAULT)); + payload.setContent(Base64.encodeToString(ciphertext,Base64.DEFAULT)); + return message; + } + + + public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key) { + XmppAxolotlPlaintextMessage plaintextMessage = null; + try { + + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + + String plaintext = new String(cipher.doFinal(ciphertext)); + plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext); + + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException + | BadPaddingException e) { + throw new AssertionError(e); + } + return plaintextMessage; + } } -- cgit v1.2.3 From 299bbdf27f0144e6eed99e70a3b2e46f9a3aa301 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Fri, 26 Jun 2015 15:41:02 +0200 Subject: Reformat code to use tabs This really sucks to do it like this. Sorry. :( --- .../crypto/axolotl/XmppAxolotlMessage.java | 320 ++++++++++----------- 1 file changed, 160 insertions(+), 160 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 4b87fc5c..e1b95650 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -21,164 +21,164 @@ import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.xml.Element; public class XmppAxolotlMessage { - private byte[] innerKey; - private byte[] ciphertext; - private byte[] iv; - private final Set headers; - private final Contact contact; - private final int sourceDeviceId; - - public static class XmppAxolotlMessageHeader { - private final int recipientDeviceId; - private final byte[] content; - - public XmppAxolotlMessageHeader(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); - } else { - throw new IllegalArgumentException("Argument not a
Element!"); - } - } - - public int getRecipientDeviceId() { - return recipientDeviceId; - } - - public byte[] getContents() { - return content; - } - - public Element toXml() { - Element headerElement = new Element("header"); - // TODO: generate XML - headerElement.setAttribute("rid", getRecipientDeviceId()); - headerElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT)); - return headerElement; - } - } - - public static class XmppAxolotlPlaintextMessage { - private final AxolotlService.XmppAxolotlSession session; - private final String plaintext; - - public XmppAxolotlPlaintextMessage(AxolotlService.XmppAxolotlSession session, String plaintext) { - this.session = session; - this.plaintext = plaintext; - } - - public String getPlaintext() { - return plaintext; - } - } - - public XmppAxolotlMessage(Contact contact, Element axolotlMessage) { - this.contact = contact; - 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)); - break; - case "message": - iv = Base64.decode(child.getAttribute("iv"),Base64.DEFAULT); - ciphertext = Base64.decode(child.getContent(),Base64.DEFAULT); - break; - default: - break; - } - } - } - - public XmppAxolotlMessage(Contact contact, int sourceDeviceId, String plaintext) { - this.contact = contact; - this.sourceDeviceId = sourceDeviceId; - this.headers = new HashSet<>(); - this.encrypt(plaintext); - } - - private void encrypt(String plaintext) { - try { - KeyGenerator generator = KeyGenerator.getInstance("AES"); - generator.init(128); - SecretKey secretKey = generator.generateKey(); - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); - this.innerKey = secretKey.getEncoded(); - this.iv = cipher.getIV(); - this.ciphertext = cipher.doFinal(plaintext.getBytes()); - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException - | IllegalBlockSizeException | BadPaddingException e) { - - } - } - - public Contact getContact() { - return this.contact; - } - - public int getSenderDeviceId() { - return sourceDeviceId; - } - - public byte[] getCiphertext() { - return ciphertext; - } - - public Set getHeaders() { - return headers; - } - - public void addHeader(XmppAxolotlMessageHeader header) { - headers.add(header); - } - - public byte[] getInnerKey(){ - return innerKey; - } - - public byte[] getIV() { - return this.iv; - } - - 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 payload = message.addChild("message"); - payload.setAttribute("iv",Base64.encodeToString(iv, Base64.DEFAULT)); - payload.setContent(Base64.encodeToString(ciphertext,Base64.DEFAULT)); - return message; - } - - - public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key) { - XmppAxolotlPlaintextMessage plaintextMessage = null; - try { - - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); - IvParameterSpec ivSpec = new IvParameterSpec(iv); - - cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); - - String plaintext = new String(cipher.doFinal(ciphertext)); - plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext); - - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException - | InvalidAlgorithmParameterException | IllegalBlockSizeException - | BadPaddingException e) { - throw new AssertionError(e); - } - return plaintextMessage; - } + private byte[] innerKey; + private byte[] ciphertext; + private byte[] iv; + private final Set headers; + private final Contact contact; + private final int sourceDeviceId; + + public static class XmppAxolotlMessageHeader { + private final int recipientDeviceId; + private final byte[] content; + + public XmppAxolotlMessageHeader(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); + } else { + throw new IllegalArgumentException("Argument not a
Element!"); + } + } + + public int getRecipientDeviceId() { + return recipientDeviceId; + } + + public byte[] getContents() { + return content; + } + + public Element toXml() { + Element headerElement = new Element("header"); + // TODO: generate XML + headerElement.setAttribute("rid", getRecipientDeviceId()); + headerElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT)); + return headerElement; + } + } + + public static class XmppAxolotlPlaintextMessage { + private final AxolotlService.XmppAxolotlSession session; + private final String plaintext; + + public XmppAxolotlPlaintextMessage(AxolotlService.XmppAxolotlSession session, String plaintext) { + this.session = session; + this.plaintext = plaintext; + } + + public String getPlaintext() { + return plaintext; + } + } + + public XmppAxolotlMessage(Contact contact, Element axolotlMessage) { + this.contact = contact; + 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)); + break; + case "message": + iv = Base64.decode(child.getAttribute("iv"),Base64.DEFAULT); + ciphertext = Base64.decode(child.getContent(),Base64.DEFAULT); + break; + default: + break; + } + } + } + + public XmppAxolotlMessage(Contact contact, int sourceDeviceId, String plaintext) { + this.contact = contact; + this.sourceDeviceId = sourceDeviceId; + this.headers = new HashSet<>(); + this.encrypt(plaintext); + } + + private void encrypt(String plaintext) { + try { + KeyGenerator generator = KeyGenerator.getInstance("AES"); + generator.init(128); + SecretKey secretKey = generator.generateKey(); + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); + this.innerKey = secretKey.getEncoded(); + this.iv = cipher.getIV(); + this.ciphertext = cipher.doFinal(plaintext.getBytes()); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | IllegalBlockSizeException | BadPaddingException e) { + + } + } + + public Contact getContact() { + return this.contact; + } + + public int getSenderDeviceId() { + return sourceDeviceId; + } + + public byte[] getCiphertext() { + return ciphertext; + } + + public Set getHeaders() { + return headers; + } + + public void addHeader(XmppAxolotlMessageHeader header) { + headers.add(header); + } + + public byte[] getInnerKey(){ + return innerKey; + } + + public byte[] getIV() { + return this.iv; + } + + 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 payload = message.addChild("message"); + payload.setAttribute("iv",Base64.encodeToString(iv, Base64.DEFAULT)); + payload.setContent(Base64.encodeToString(ciphertext,Base64.DEFAULT)); + return message; + } + + + public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key) { + XmppAxolotlPlaintextMessage plaintextMessage = null; + try { + + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + + String plaintext = new String(cipher.doFinal(ciphertext)); + plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext); + + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException + | BadPaddingException e) { + throw new AssertionError(e); + } + return plaintextMessage; + } } -- cgit v1.2.3 From 1b0596d57473f7aafa633ed2d5f3b3610a653a51 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Mon, 29 Jun 2015 14:25:23 +0200 Subject: Tag messages with originating session This can be used later in order to display trust status of messages, as well as for potential resending of messages in case of preKey conflicts. --- .../eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 e1b95650..01b06c22 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -75,6 +75,11 @@ public class XmppAxolotlMessage { public String getPlaintext() { return plaintext; } + + public AxolotlService.XmppAxolotlSession getSession() { + return session; + } + } public XmppAxolotlMessage(Contact contact, Element axolotlMessage) { -- cgit v1.2.3 From ec6870307e0ecee8184ddfef73444290e9d15828 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Fri, 3 Jul 2015 13:27:35 +0200 Subject: Properly track message sender Previously, the sender was assumed to be the conversation counterpart. This broke carboned own-device messages. We now track the sender properly, and also set the status (sent by one of the own devices vs received from the counterpart) accordingly. --- .../conversations/crypto/axolotl/XmppAxolotlMessage.java | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 01b06c22..06dd2cda 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -19,13 +19,14 @@ import javax.crypto.spec.SecretKeySpec; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.xml.Element; +import eu.siacs.conversations.xmpp.jid.Jid; public class XmppAxolotlMessage { private byte[] innerKey; private byte[] ciphertext; private byte[] iv; private final Set headers; - private final Contact contact; + private final Jid from; private final int sourceDeviceId; public static class XmppAxolotlMessageHeader { @@ -82,8 +83,8 @@ public class XmppAxolotlMessage { } - public XmppAxolotlMessage(Contact contact, Element axolotlMessage) { - this.contact = contact; + public XmppAxolotlMessage(Jid from, Element axolotlMessage) { + this.from = from; this.sourceDeviceId = Integer.parseInt(axolotlMessage.getAttribute("id")); this.headers = new HashSet<>(); for(Element child:axolotlMessage.getChildren()) { @@ -101,8 +102,8 @@ public class XmppAxolotlMessage { } } - public XmppAxolotlMessage(Contact contact, int sourceDeviceId, String plaintext) { - this.contact = contact; + public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) { + this.from = from; this.sourceDeviceId = sourceDeviceId; this.headers = new HashSet<>(); this.encrypt(plaintext); @@ -124,8 +125,8 @@ public class XmppAxolotlMessage { } } - public Contact getContact() { - return this.contact; + public Jid getFrom() { + return this.from; } public int getSenderDeviceId() { -- cgit v1.2.3 From d173913ebabbf8de2725dd296ae6428defd4b3b3 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Thu, 9 Jul 2015 14:23:17 +0200 Subject: Overhauled Message tagging Messages are now tagged with the IdentityKey fingerprint of the originating session. IdentityKeys have one of three trust states: undecided (default), trusted, and untrusted/not yet trusted. --- .../conversations/crypto/axolotl/XmppAxolotlMessage.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 06dd2cda..45995228 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -67,10 +67,12 @@ public class XmppAxolotlMessage { public static class XmppAxolotlPlaintextMessage { private final AxolotlService.XmppAxolotlSession session; private final String plaintext; + private final String fingerprint; - public XmppAxolotlPlaintextMessage(AxolotlService.XmppAxolotlSession session, String plaintext) { + public XmppAxolotlPlaintextMessage(AxolotlService.XmppAxolotlSession session, String plaintext, String fingerprint) { this.session = session; this.plaintext = plaintext; + this.fingerprint = fingerprint; } public String getPlaintext() { @@ -81,6 +83,9 @@ public class XmppAxolotlMessage { return session; } + public String getFingerprint() { + return fingerprint; + } } public XmppAxolotlMessage(Jid from, Element axolotlMessage) { @@ -167,7 +172,7 @@ public class XmppAxolotlMessage { } - public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key) { + public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key, String fingerprint) { XmppAxolotlPlaintextMessage plaintextMessage = null; try { @@ -178,7 +183,7 @@ public class XmppAxolotlMessage { cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); String plaintext = new String(cipher.doFinal(ciphertext)); - plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext); + plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext, fingerprint); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException -- cgit v1.2.3 From 012f036840ade8f46462eafcc96d1f223f8ba845 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Mon, 20 Jul 2015 14:26:29 +0200 Subject: Optimize imports --- .../java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 45995228..1378c94a 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -3,8 +3,8 @@ package eu.siacs.conversations.crypto.axolotl; import android.util.Base64; import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; import java.util.HashSet; import java.util.Set; @@ -17,7 +17,6 @@ import javax.crypto.SecretKey; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; -import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.xml.Element; import eu.siacs.conversations.xmpp.jid.Jid; -- cgit v1.2.3 From ab2d114bbc21a5c2d684f8760cb8e4cea54be5de Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Mon, 20 Jul 2015 22:18:24 +0200 Subject: Add purge axolotl key option Can now long-press a key to permanently purge it. --- .../eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 1378c94a..ec068ec7 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -1,5 +1,6 @@ package eu.siacs.conversations.crypto.axolotl; +import android.support.annotation.Nullable; import android.util.Base64; import java.security.InvalidAlgorithmParameterException; @@ -145,8 +146,10 @@ public class XmppAxolotlMessage { return headers; } - public void addHeader(XmppAxolotlMessageHeader header) { - headers.add(header); + public void addHeader(@Nullable XmppAxolotlMessageHeader header) { + if (header != null) { + headers.add(header); + } } public byte[] getInnerKey(){ -- cgit v1.2.3 From 122bc97ce24181ccd07cf9badf8d4c3b81d80c3f Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Tue, 21 Jul 2015 01:15:32 +0200 Subject: Switch payload encryption to AES-GCM This also ensures that the IV is generated with proper randomness. --- .../crypto/axolotl/XmppAxolotlMessage.java | 28 +++++++++++++--------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 ec068ec7..24afeaea 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -6,6 +6,8 @@ import android.util.Base64; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; import java.util.HashSet; import java.util.Set; @@ -107,26 +109,30 @@ public class XmppAxolotlMessage { } } - public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) { + public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) throws CryptoFailedException{ this.from = from; this.sourceDeviceId = sourceDeviceId; this.headers = new HashSet<>(); this.encrypt(plaintext); } - private void encrypt(String plaintext) { + private void encrypt(String plaintext) throws CryptoFailedException { try { KeyGenerator generator = KeyGenerator.getInstance("AES"); generator.init(128); SecretKey secretKey = generator.generateKey(); - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); + SecureRandom random = new SecureRandom(); + this.iv = new byte[16]; + random.nextBytes(iv); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); this.innerKey = secretKey.getEncoded(); - this.iv = cipher.getIV(); this.ciphertext = cipher.doFinal(plaintext.getBytes()); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException - | IllegalBlockSizeException | BadPaddingException e) { - + | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException + | InvalidAlgorithmParameterException e) { + throw new CryptoFailedException(e); } } @@ -174,11 +180,11 @@ public class XmppAxolotlMessage { } - public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key, String fingerprint) { + public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key, String fingerprint) throws CryptoFailedException { XmppAxolotlPlaintextMessage plaintextMessage = null; try { - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); @@ -189,8 +195,8 @@ public class XmppAxolotlMessage { } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException - | BadPaddingException e) { - throw new AssertionError(e); + | BadPaddingException | NoSuchProviderException e) { + throw new CryptoFailedException(e); } return plaintextMessage; } -- cgit v1.2.3 From efcefc2e6301f6255ebcb3e11f0f0a51731bacca Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Tue, 28 Jul 2015 22:00:54 +0200 Subject: Refactor out inner classes, cache trust store Moves SQLiteAxolotlStore and XmppAxolotlSession into proper classes. IdentityKeys trust statuses are now cached in an LruCache to prevent hammering the database when rendering the UI. --- .../eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 24afeaea..182c8f12 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -67,11 +67,11 @@ public class XmppAxolotlMessage { } public static class XmppAxolotlPlaintextMessage { - private final AxolotlService.XmppAxolotlSession session; + private final XmppAxolotlSession session; private final String plaintext; private final String fingerprint; - public XmppAxolotlPlaintextMessage(AxolotlService.XmppAxolotlSession session, String plaintext, String fingerprint) { + public XmppAxolotlPlaintextMessage(XmppAxolotlSession session, String plaintext, String fingerprint) { this.session = session; this.plaintext = plaintext; this.fingerprint = fingerprint; @@ -81,7 +81,7 @@ public class XmppAxolotlMessage { return plaintext; } - public AxolotlService.XmppAxolotlSession getSession() { + public XmppAxolotlSession getSession() { return session; } @@ -180,7 +180,7 @@ public class XmppAxolotlMessage { } - public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key, String fingerprint) throws CryptoFailedException { + public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, byte[] key, String fingerprint) throws CryptoFailedException { XmppAxolotlPlaintextMessage plaintextMessage = null; try { -- cgit v1.2.3 From 5c421da1e1bd4c6008c36b17569c3dbc5d5e918b Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Fri, 31 Jul 2015 17:59:41 +0200 Subject: Change to new wire protocol version --- .../crypto/axolotl/XmppAxolotlMessage.java | 143 ++++++++++++++------- 1 file changed, 93 insertions(+), 50 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 headers; + private byte[] ciphertext = null; + private byte[] iv = null; + private final Set 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
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 getHeaders() { - return headers; + public Set 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); -- cgit v1.2.3 From 50b14434eeda183d1d197a378239654a8db8b3a8 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Fri, 31 Jul 2015 18:05:32 +0200 Subject: Reformat code --- .../crypto/axolotl/XmppAxolotlMessage.java | 28 +++++++++++----------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 4725ce8a..4e954a72 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -56,11 +56,11 @@ public class XmppAxolotlMessage { } public XmppAxolotlKeyElement(Element keyElement) { - if(TAGNAME.equals(keyElement.getName())) { + if (TAGNAME.equals(keyElement.getName())) { this.recipientDeviceId = Integer.parseInt(keyElement.getAttribute(REMOTEID)); - this.content = Base64.decode(keyElement.getContent(),Base64.DEFAULT); + this.content = Base64.decode(keyElement.getContent(), Base64.DEFAULT); } else { - throw new IllegalArgumentException("Argument not a <"+TAGNAME+"> Element!"); + throw new IllegalArgumentException("Argument not a <" + TAGNAME + "> Element!"); } } @@ -109,24 +109,24 @@ public class XmppAxolotlMessage { Element header = axolotlMessage.findChild(HEADER); this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID)); this.keyElements = new HashSet<>(); - for(Element keyElement:header.getChildren()) { - switch(keyElement.getName()) { + for (Element keyElement : header.getChildren()) { + switch (keyElement.getName()) { case XmppAxolotlKeyElement.TAGNAME: keyElements.add(new XmppAxolotlKeyElement(keyElement)); break; case IVTAG: - if ( this.iv != null) { + if (this.iv != null) { throw new IllegalArgumentException("Duplicate iv entry"); } - iv = Base64.decode(keyElement.getContent(),Base64.DEFAULT); + iv = Base64.decode(keyElement.getContent(), Base64.DEFAULT); break; default: - Log.w(Config.LOGTAG, "Unexpected element in header: "+ keyElement.toString()); + Log.w(Config.LOGTAG, "Unexpected element in header: " + keyElement.toString()); break; } } Element payloadElement = axolotlMessage.findChild(PAYLOAD); - if ( payloadElement != null ) { + if (payloadElement != null) { ciphertext = Base64.decode(payloadElement.getContent(), Base64.DEFAULT); } } @@ -139,7 +139,7 @@ public class XmppAxolotlMessage { this.innerKey = generateKey(); } - public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) throws CryptoFailedException{ + public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) throws CryptoFailedException { this(from, sourceDeviceId); this.encrypt(plaintext); } @@ -199,7 +199,7 @@ public class XmppAxolotlMessage { } } - public byte[] getInnerKey(){ + public byte[] getInnerKey() { return innerKey; } @@ -208,14 +208,14 @@ public class XmppAxolotlMessage { } public Element toXml() { - Element encryptionElement= new Element(TAGNAME, AxolotlService.PEP_PREFIX); + Element encryptionElement = new Element(TAGNAME, AxolotlService.PEP_PREFIX); Element headerElement = encryptionElement.addChild(HEADER); headerElement.setAttribute(SOURCEID, sourceDeviceId); - for(XmppAxolotlKeyElement header: keyElements) { + for (XmppAxolotlKeyElement header : keyElements) { headerElement.addChild(header.toXml()); } headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.DEFAULT)); - if ( ciphertext != null ) { + if (ciphertext != null) { Element payload = encryptionElement.addChild(PAYLOAD); payload.setContent(Base64.encodeToString(ciphertext, Base64.DEFAULT)); } -- cgit v1.2.3 From 909f761ca1659938cf5f9d7206ee24d54faa8550 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Fri, 31 Jul 2015 21:12:34 +0200 Subject: Refactor axolotl message processing workflow XmppAxolotlMessage is now entirely responsible for handling encryption and decryption of messages, only leveraging XmppAxolotlSession as a packing/unpacking primitive for payload keys. Removed pseudo-dead session generation code step from prepareMessage function, as sessions have been created by invoking the TrustKeysActivity for a while now. Added prepareKeyTransportMessage function, which creates a message with no payload. The key that is packed into the header keyElements can then be used for other purposes (e.g. encrypted file transfer). --- .../crypto/axolotl/XmppAxolotlMessage.java | 142 +++++++++------------ 1 file changed, 57 insertions(+), 85 deletions(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 4e954a72..fa6d895a 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -1,6 +1,5 @@ package eu.siacs.conversations.crypto.axolotl; -import android.support.annotation.Nullable; import android.util.Base64; import android.util.Log; @@ -9,8 +8,9 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; -import java.util.HashSet; -import java.util.Set; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -26,9 +26,11 @@ 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 CONTAINERTAG = "encrypted"; public static final String HEADER = "header"; public static final String SOURCEID = "sid"; + public static final String KEYTAG = "key"; + public static final String REMOTEID = "rid"; public static final String IVTAG = "iv"; public static final String PAYLOAD = "payload"; @@ -39,54 +41,15 @@ public class XmppAxolotlMessage { private byte[] innerKey; private byte[] ciphertext = null; private byte[] iv = null; - private final Set keyElements; + private final Map keys; private final Jid from; private final int sourceDeviceId; - 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 XmppAxolotlKeyElement(int deviceId, byte[] content) { - this.recipientDeviceId = deviceId; - this.content = content; - } - - 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 <" + TAGNAME + "> Element!"); - } - } - - public int getRecipientDeviceId() { - return recipientDeviceId; - } - - public byte[] getContents() { - return content; - } - - public Element toXml() { - Element keyElement = new Element(TAGNAME); - keyElement.setAttribute(REMOTEID, getRecipientDeviceId()); - keyElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT)); - return keyElement; - } - } - public static class XmppAxolotlPlaintextMessage { - private final XmppAxolotlSession session; private final String plaintext; private final String fingerprint; - public XmppAxolotlPlaintextMessage(XmppAxolotlSession session, String plaintext, String fingerprint) { - this.session = session; + public XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) { this.plaintext = plaintext; this.fingerprint = fingerprint; } @@ -95,24 +58,28 @@ public class XmppAxolotlMessage { return plaintext; } - public XmppAxolotlSession getSession() { - return session; - } public String getFingerprint() { return fingerprint; } } - public XmppAxolotlMessage(Jid from, Element axolotlMessage) throws IllegalArgumentException { + private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException { this.from = from; Element header = axolotlMessage.findChild(HEADER); this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID)); - this.keyElements = new HashSet<>(); - for (Element keyElement : header.getChildren()) { + List keyElements = header.getChildren(); + this.keys = new HashMap<>(keyElements.size()); + for (Element keyElement : keyElements) { switch (keyElement.getName()) { - case XmppAxolotlKeyElement.TAGNAME: - keyElements.add(new XmppAxolotlKeyElement(keyElement)); + case KEYTAG: + try { + Integer recipientId = Integer.parseInt(keyElement.getAttribute(REMOTEID)); + byte[] key = Base64.decode(keyElement.getContent(), Base64.DEFAULT); + this.keys.put(recipientId, key); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(e); + } break; case IVTAG: if (this.iv != null) { @@ -134,14 +101,13 @@ public class XmppAxolotlMessage { public XmppAxolotlMessage(Jid from, int sourceDeviceId) { this.from = from; this.sourceDeviceId = sourceDeviceId; - this.keyElements = new HashSet<>(); + this.keys = new HashMap<>(); this.iv = generateIv(); this.innerKey = generateKey(); } - public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) throws CryptoFailedException { - this(from, sourceDeviceId); - this.encrypt(plaintext); + public static XmppAxolotlMessage fromElement(Element element, Jid from) { + return new XmppAxolotlMessage(element, from); } private static byte[] generateKey() { @@ -162,7 +128,7 @@ public class XmppAxolotlMessage { return iv; } - private void encrypt(String plaintext) throws CryptoFailedException { + public void encrypt(String plaintext) throws CryptoFailedException { try { SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE); IvParameterSpec ivSpec = new IvParameterSpec(iv); @@ -189,13 +155,10 @@ public class XmppAxolotlMessage { return ciphertext; } - public Set getKeyElements() { - return keyElements; - } - - public void addKeyElement(@Nullable XmppAxolotlKeyElement keyElement) { - if (keyElement != null) { - keyElements.add(keyElement); + public void addDevice(XmppAxolotlSession session) { + byte[] key = session.processSending(innerKey); + if (key != null) { + keys.put(session.getRemoteAddress().getDeviceId(), key); } } @@ -207,12 +170,15 @@ public class XmppAxolotlMessage { return this.iv; } - public Element toXml() { - Element encryptionElement = new Element(TAGNAME, AxolotlService.PEP_PREFIX); + public Element toElement() { + Element encryptionElement = new Element(CONTAINERTAG, AxolotlService.PEP_PREFIX); Element headerElement = encryptionElement.addChild(HEADER); headerElement.setAttribute(SOURCEID, sourceDeviceId); - for (XmppAxolotlKeyElement header : keyElements) { - headerElement.addChild(header.toXml()); + for (Map.Entry keyEntry : keys.entrySet()) { + Element keyElement = new Element(KEYTAG); + keyElement.setAttribute(REMOTEID, keyEntry.getKey()); + keyElement.setContent(Base64.encodeToString(keyEntry.getValue(), Base64.DEFAULT)); + headerElement.addChild(keyElement); } headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.DEFAULT)); if (ciphertext != null) { @@ -222,24 +188,30 @@ public class XmppAxolotlMessage { return encryptionElement; } + public byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) { + byte[] encryptedKey = keys.get(sourceDeviceId); + return (encryptedKey != null) ? session.processReceiving(encryptedKey) : null; + } - public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, byte[] key, String fingerprint) throws CryptoFailedException { + public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException { XmppAxolotlPlaintextMessage plaintextMessage = null; - try { - - Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER); - SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE); - IvParameterSpec ivSpec = new IvParameterSpec(iv); - - cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); - - String plaintext = new String(cipher.doFinal(ciphertext)); - plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext, fingerprint); - - } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException - | InvalidAlgorithmParameterException | IllegalBlockSizeException - | BadPaddingException | NoSuchProviderException e) { - throw new CryptoFailedException(e); + byte[] key = unpackKey(session, sourceDeviceId); + if (key != null) { + try { + Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER); + SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); + + String plaintext = new String(cipher.doFinal(ciphertext)); + plaintextMessage = new XmppAxolotlPlaintextMessage(plaintext, session.getFingerprint()); + + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException + | InvalidAlgorithmParameterException | IllegalBlockSizeException + | BadPaddingException | NoSuchProviderException e) { + throw new CryptoFailedException(e); + } } return plaintextMessage; } -- cgit v1.2.3 From 6059b964569fb406bbc86a0ccb19e76851fba2b6 Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Fri, 31 Jul 2015 23:28:09 +0200 Subject: Provide process function for key transport message --- .../crypto/axolotl/XmppAxolotlMessage.java | 33 +++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) (limited to 'src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java') 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 fa6d895a..cf950d6d 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -64,6 +64,30 @@ public class XmppAxolotlMessage { } } + public static class XmppAxolotlKeyTransportMessage { + private final String fingerprint; + private final byte[] key; + private final byte[] iv; + + public XmppAxolotlKeyTransportMessage(String fingerprint, byte[] key, byte[] iv) { + this.fingerprint = fingerprint; + this.key = key; + this.iv = iv; + } + + public String getFingerprint() { + return fingerprint; + } + + public byte[] getKey() { + return key; + } + + public byte[] getIv() { + return iv; + } + } + private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException { this.from = from; Element header = axolotlMessage.findChild(HEADER); @@ -188,11 +212,18 @@ public class XmppAxolotlMessage { return encryptionElement; } - public byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) { + private byte[] unpackKey(XmppAxolotlSession session, Integer sourceDeviceId) { byte[] encryptedKey = keys.get(sourceDeviceId); return (encryptedKey != null) ? session.processReceiving(encryptedKey) : null; } + public XmppAxolotlKeyTransportMessage getParameters(XmppAxolotlSession session, Integer sourceDeviceId) { + byte[] key = unpackKey(session, sourceDeviceId); + return (key != null) + ? new XmppAxolotlKeyTransportMessage(session.getFingerprint(), key, getIV()) + : null; + } + public XmppAxolotlPlaintextMessage decrypt(XmppAxolotlSession session, Integer sourceDeviceId) throws CryptoFailedException { XmppAxolotlPlaintextMessage plaintextMessage = null; byte[] key = unpackKey(session, sourceDeviceId); -- cgit v1.2.3