Change to new wire protocol version
This commit is contained in:
parent
26ac7c9030
commit
5c421da1e1
|
@ -583,23 +583,15 @@ public class AxolotlService {
|
||||||
if(findSessionsforContact(message.getContact()).isEmpty()) {
|
if(findSessionsforContact(message.getContact()).isEmpty()) {
|
||||||
return null;
|
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())) {
|
for (XmppAxolotlSession session : findSessionsforContact(message.getContact())) {
|
||||||
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account)+session.getRemoteAddress().toString());
|
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account)+session.getRemoteAddress().toString());
|
||||||
//if(!session.isTrusted()) {
|
axolotlMessage.addKeyElement(session.processSending(axolotlMessage.getInnerKey()));
|
||||||
// TODO: handle this properly
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
axolotlMessage.addHeader(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()) {
|
for (XmppAxolotlSession session : findOwnSessions()) {
|
||||||
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account)+session.getRemoteAddress().toString());
|
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account)+session.getRemoteAddress().toString());
|
||||||
// if(!session.isTrusted()) {
|
axolotlMessage.addKeyElement(session.processSending(axolotlMessage.getInnerKey()));
|
||||||
// TODO: handle this properly
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
axolotlMessage.addHeader(session.processSending(axolotlMessage.getInnerKey()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return axolotlMessage;
|
return axolotlMessage;
|
||||||
|
@ -651,7 +643,6 @@ public class AxolotlService {
|
||||||
XmppAxolotlSession session = sessions.get(senderAddress);
|
XmppAxolotlSession session = sessions.get(senderAddress);
|
||||||
if (session == null) {
|
if (session == null) {
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Account: "+account.getJid()+" No axolotl session found while parsing received message " + message);
|
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();
|
IdentityKey identityKey = axolotlStore.loadSession(senderAddress).getSessionState().getRemoteIdentityKey();
|
||||||
if ( identityKey != null ) {
|
if ( identityKey != null ) {
|
||||||
session = new XmppAxolotlSession(account, axolotlStore, senderAddress, identityKey.getFingerprint().replaceAll("\\s", ""));
|
session = new XmppAxolotlSession(account, axolotlStore, senderAddress, identityKey.getFingerprint().replaceAll("\\s", ""));
|
||||||
|
@ -661,12 +652,12 @@ public class AxolotlService {
|
||||||
newSession = true;
|
newSession = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (XmppAxolotlMessage.XmppAxolotlMessageHeader header : message.getHeaders()) {
|
for (XmppAxolotlMessage.XmppAxolotlKeyElement keyElement : message.getKeyElements()) {
|
||||||
if (header.getRecipientDeviceId() == getOwnDeviceId()) {
|
if (keyElement.getRecipientDeviceId() == getOwnDeviceId()) {
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Found axolotl header matching own device ID, processing...");
|
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Found axolotl keyElement matching own device ID, processing...");
|
||||||
byte[] payloadKey = session.processReceiving(header);
|
byte[] payloadKey = session.processReceiving(keyElement);
|
||||||
if (payloadKey != null) {
|
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{
|
try{
|
||||||
plaintextMessage = message.decrypt(session, payloadKey, session.getFingerprint());
|
plaintextMessage = message.decrypt(session, payloadKey, session.getFingerprint());
|
||||||
} catch (CryptoFailedException e) {
|
} catch (CryptoFailedException e) {
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package eu.siacs.conversations.crypto.axolotl;
|
||||||
|
|
||||||
|
public interface OnMessageCreatedCallback {
|
||||||
|
void run(XmppAxolotlMessage message);
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ package eu.siacs.conversations.crypto.axolotl;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
@ -20,32 +21,46 @@ import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
import eu.siacs.conversations.xml.Element;
|
import eu.siacs.conversations.xml.Element;
|
||||||
import eu.siacs.conversations.xmpp.jid.Jid;
|
import eu.siacs.conversations.xmpp.jid.Jid;
|
||||||
|
|
||||||
public class XmppAxolotlMessage {
|
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[] innerKey;
|
||||||
private byte[] ciphertext;
|
private byte[] ciphertext = null;
|
||||||
private byte[] iv;
|
private byte[] iv = null;
|
||||||
private final Set<XmppAxolotlMessageHeader> headers;
|
private final Set<XmppAxolotlKeyElement> keyElements;
|
||||||
private final Jid from;
|
private final Jid from;
|
||||||
private final int sourceDeviceId;
|
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 int recipientDeviceId;
|
||||||
private final byte[] content;
|
private final byte[] content;
|
||||||
|
|
||||||
public XmppAxolotlMessageHeader(int deviceId, byte[] content) {
|
public XmppAxolotlKeyElement(int deviceId, byte[] content) {
|
||||||
this.recipientDeviceId = deviceId;
|
this.recipientDeviceId = deviceId;
|
||||||
this.content = content;
|
this.content = content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmppAxolotlMessageHeader(Element header) {
|
public XmppAxolotlKeyElement(Element keyElement) {
|
||||||
if("header".equals(header.getName())) {
|
if(TAGNAME.equals(keyElement.getName())) {
|
||||||
this.recipientDeviceId = Integer.parseInt(header.getAttribute("rid"));
|
this.recipientDeviceId = Integer.parseInt(keyElement.getAttribute(REMOTEID));
|
||||||
this.content = Base64.decode(header.getContent(),Base64.DEFAULT);
|
this.content = Base64.decode(keyElement.getContent(),Base64.DEFAULT);
|
||||||
} else {
|
} 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() {
|
public Element toXml() {
|
||||||
Element headerElement = new Element("header");
|
Element keyElement = new Element(TAGNAME);
|
||||||
// TODO: generate XML
|
keyElement.setAttribute(REMOTEID, getRecipientDeviceId());
|
||||||
headerElement.setAttribute("rid", getRecipientDeviceId());
|
keyElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT));
|
||||||
headerElement.setContent(Base64.encodeToString(getContents(), Base64.DEFAULT));
|
return keyElement;
|
||||||
return headerElement;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.from = from;
|
||||||
this.sourceDeviceId = Integer.parseInt(axolotlMessage.getAttribute("id"));
|
Element header = axolotlMessage.findChild(HEADER);
|
||||||
this.headers = new HashSet<>();
|
this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
|
||||||
for(Element child:axolotlMessage.getChildren()) {
|
this.keyElements = new HashSet<>();
|
||||||
switch(child.getName()) {
|
for(Element keyElement:header.getChildren()) {
|
||||||
case "header":
|
switch(keyElement.getName()) {
|
||||||
headers.add(new XmppAxolotlMessageHeader(child));
|
case XmppAxolotlKeyElement.TAGNAME:
|
||||||
|
keyElements.add(new XmppAxolotlKeyElement(keyElement));
|
||||||
break;
|
break;
|
||||||
case "message":
|
case IVTAG:
|
||||||
iv = Base64.decode(child.getAttribute("iv"),Base64.DEFAULT);
|
if ( this.iv != null) {
|
||||||
ciphertext = Base64.decode(child.getContent(),Base64.DEFAULT);
|
throw new IllegalArgumentException("Duplicate iv entry");
|
||||||
|
}
|
||||||
|
iv = Base64.decode(keyElement.getContent(),Base64.DEFAULT);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
Log.w(Config.LOGTAG, "Unexpected element in header: "+ keyElement.toString());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Element payloadElement = axolotlMessage.findChild(PAYLOAD);
|
||||||
|
if ( payloadElement != null ) {
|
||||||
|
ciphertext = Base64.decode(payloadElement.getContent(), Base64.DEFAULT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
|
||||||
|
this.from = from;
|
||||||
|
this.sourceDeviceId = sourceDeviceId;
|
||||||
|
this.keyElements = new HashSet<>();
|
||||||
|
this.iv = generateIv();
|
||||||
|
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 = from;
|
this(from, sourceDeviceId);
|
||||||
this.sourceDeviceId = sourceDeviceId;
|
|
||||||
this.headers = new HashSet<>();
|
|
||||||
this.encrypt(plaintext);
|
this.encrypt(plaintext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static byte[] generateKey() {
|
||||||
|
try {
|
||||||
|
KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
|
||||||
|
generator.init(128);
|
||||||
|
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 {
|
private void encrypt(String plaintext) throws CryptoFailedException {
|
||||||
try {
|
try {
|
||||||
KeyGenerator generator = KeyGenerator.getInstance("AES");
|
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
|
||||||
generator.init(128);
|
|
||||||
SecretKey secretKey = generator.generateKey();
|
|
||||||
SecureRandom random = new SecureRandom();
|
|
||||||
this.iv = new byte[16];
|
|
||||||
random.nextBytes(iv);
|
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
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);
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
|
||||||
this.innerKey = secretKey.getEncoded();
|
this.innerKey = secretKey.getEncoded();
|
||||||
this.ciphertext = cipher.doFinal(plaintext.getBytes());
|
this.ciphertext = cipher.doFinal(plaintext.getBytes());
|
||||||
|
@ -148,13 +189,13 @@ public class XmppAxolotlMessage {
|
||||||
return ciphertext;
|
return ciphertext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<XmppAxolotlMessageHeader> getHeaders() {
|
public Set<XmppAxolotlKeyElement> getKeyElements() {
|
||||||
return headers;
|
return keyElements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addHeader(@Nullable XmppAxolotlMessageHeader header) {
|
public void addKeyElement(@Nullable XmppAxolotlKeyElement keyElement) {
|
||||||
if (header != null) {
|
if (keyElement != null) {
|
||||||
headers.add(header);
|
keyElements.add(keyElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,16 +208,18 @@ public class XmppAxolotlMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element toXml() {
|
public Element toXml() {
|
||||||
// TODO: generate outer XML, add in header XML
|
Element encryptionElement= new Element(TAGNAME, AxolotlService.PEP_PREFIX);
|
||||||
Element message= new Element("axolotl_message", AxolotlService.PEP_PREFIX);
|
Element headerElement = encryptionElement.addChild(HEADER);
|
||||||
message.setAttribute("id", sourceDeviceId);
|
headerElement.setAttribute(SOURCEID, sourceDeviceId);
|
||||||
for(XmppAxolotlMessageHeader header: headers) {
|
for(XmppAxolotlKeyElement header: keyElements) {
|
||||||
message.addChild(header.toXml());
|
headerElement.addChild(header.toXml());
|
||||||
}
|
}
|
||||||
Element payload = message.addChild("message");
|
headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.DEFAULT));
|
||||||
payload.setAttribute("iv",Base64.encodeToString(iv, Base64.DEFAULT));
|
if ( ciphertext != null ) {
|
||||||
payload.setContent(Base64.encodeToString(ciphertext,Base64.DEFAULT));
|
Element payload = encryptionElement.addChild(PAYLOAD);
|
||||||
return message;
|
payload.setContent(Base64.encodeToString(ciphertext, Base64.DEFAULT));
|
||||||
|
}
|
||||||
|
return encryptionElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -184,8 +227,8 @@ public class XmppAxolotlMessage {
|
||||||
XmppAxolotlPlaintextMessage plaintextMessage = null;
|
XmppAxolotlPlaintextMessage plaintextMessage = null;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
|
Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
|
SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
|
|
||||||
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class XmppAxolotlSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlMessageHeader incomingHeader) {
|
public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlKeyElement incomingHeader) {
|
||||||
byte[] plaintext = null;
|
byte[] plaintext = null;
|
||||||
SQLiteAxolotlStore.Trust trust = getTrust();
|
SQLiteAxolotlStore.Trust trust = getTrust();
|
||||||
switch (trust) {
|
switch (trust) {
|
||||||
|
@ -117,12 +117,12 @@ public class XmppAxolotlSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public XmppAxolotlMessage.XmppAxolotlMessageHeader processSending(@NonNull byte[] outgoingMessage) {
|
public XmppAxolotlMessage.XmppAxolotlKeyElement processSending(@NonNull byte[] outgoingMessage) {
|
||||||
SQLiteAxolotlStore.Trust trust = getTrust();
|
SQLiteAxolotlStore.Trust trust = getTrust();
|
||||||
if (trust == SQLiteAxolotlStore.Trust.TRUSTED) {
|
if (trust == SQLiteAxolotlStore.Trust.TRUSTED) {
|
||||||
CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
|
CiphertextMessage ciphertextMessage = cipher.encrypt(outgoingMessage);
|
||||||
XmppAxolotlMessage.XmppAxolotlMessageHeader header =
|
XmppAxolotlMessage.XmppAxolotlKeyElement header =
|
||||||
new XmppAxolotlMessage.XmppAxolotlMessageHeader(remoteAddress.getDeviceId(),
|
new XmppAxolotlMessage.XmppAxolotlKeyElement(remoteAddress.getDeviceId(),
|
||||||
ciphertextMessage.serialize());
|
ciphertextMessage.serialize());
|
||||||
return header;
|
return header;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -272,7 +272,7 @@ public class MessageParser extends AbstractParser implements
|
||||||
final String body = packet.getBody();
|
final String body = packet.getBody();
|
||||||
final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
|
final Element mucUserElement = packet.findChild("x", "http://jabber.org/protocol/muc#user");
|
||||||
final String pgpEncrypted = packet.findChildContent("x", "jabber:x:encrypted");
|
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;
|
int status;
|
||||||
final Jid counterpart;
|
final Jid counterpart;
|
||||||
final Jid to = packet.getTo();
|
final Jid to = packet.getTo();
|
||||||
|
|
Loading…
Reference in New Issue