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).
This commit is contained in:
parent
50b14434ee
commit
909f761ca1
|
@ -256,7 +256,7 @@ public class AxolotlService {
|
||||||
for (Integer deviceId : deviceIds) {
|
for (Integer deviceId : deviceIds) {
|
||||||
AxolotlAddress ownDeviceAddress = new AxolotlAddress(jid.toBareJid().toString(), deviceId);
|
AxolotlAddress ownDeviceAddress = new AxolotlAddress(jid.toBareJid().toString(), deviceId);
|
||||||
if (sessions.get(ownDeviceAddress) == null) {
|
if (sessions.get(ownDeviceAddress) == null) {
|
||||||
buildSessionFromPEP(null, ownDeviceAddress, false);
|
buildSessionFromPEP(ownDeviceAddress);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -422,7 +422,7 @@ public class AxolotlService {
|
||||||
axolotlStore.setFingerprintTrust(fingerprint, trust);
|
axolotlStore.setFingerprintTrust(fingerprint, trust);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void buildSessionFromPEP(final Conversation conversation, final AxolotlAddress address, final boolean flushWaitingQueueAfterFetch) {
|
private void buildSessionFromPEP(final AxolotlAddress address) {
|
||||||
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.getDeviceId());
|
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building new sesstion for " + address.getDeviceId());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -434,15 +434,6 @@ public class AxolotlService {
|
||||||
AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
|
AxolotlAddress ownAddress = new AxolotlAddress(account.getJid().toBareJid().toString(), 0);
|
||||||
if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)
|
if (!fetchStatusMap.getAll(ownAddress).containsValue(FetchStatus.PENDING)
|
||||||
&& !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) {
|
&& !fetchStatusMap.getAll(address).containsValue(FetchStatus.PENDING)) {
|
||||||
if (flushWaitingQueueAfterFetch && conversation != null) {
|
|
||||||
conversation.findUnsentMessagesWithEncryption(Message.ENCRYPTION_AXOLOTL,
|
|
||||||
new Conversation.OnMessageFound() {
|
|
||||||
@Override
|
|
||||||
public void onMessageFound(Message message) {
|
|
||||||
processSending(message, false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
mXmppConnectionService.keyStatusUpdated();
|
mXmppConnectionService.keyStatusUpdated();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -537,7 +528,7 @@ public class AxolotlService {
|
||||||
return addresses;
|
return addresses;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean createSessionsIfNeeded(final Conversation conversation, final boolean flushWaitingQueueAfterFetch) {
|
public boolean createSessionsIfNeeded(final Conversation conversation) {
|
||||||
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Creating axolotl sessions if needed...");
|
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Creating axolotl sessions if needed...");
|
||||||
boolean newSessions = false;
|
boolean newSessions = false;
|
||||||
Set<AxolotlAddress> addresses = findDevicesWithoutSession(conversation);
|
Set<AxolotlAddress> addresses = findDevicesWithoutSession(conversation);
|
||||||
|
@ -546,7 +537,9 @@ public class AxolotlService {
|
||||||
FetchStatus status = fetchStatusMap.get(address);
|
FetchStatus status = fetchStatusMap.get(address);
|
||||||
if (status == null || status == FetchStatus.ERROR) {
|
if (status == null || status == FetchStatus.ERROR) {
|
||||||
fetchStatusMap.put(address, FetchStatus.PENDING);
|
fetchStatusMap.put(address, FetchStatus.PENDING);
|
||||||
this.buildSessionFromPEP(conversation, address, flushWaitingQueueAfterFetch);
|
this.buildSessionFromPEP(address);
|
||||||
|
newSessions = true;
|
||||||
|
} else if (status == FetchStatus.PENDING) {
|
||||||
newSessions = true;
|
newSessions = true;
|
||||||
} else {
|
} else {
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already fetching bundle for " + address.toString());
|
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Already fetching bundle for " + address.toString());
|
||||||
|
@ -565,40 +558,52 @@ public class AxolotlService {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public XmppAxolotlMessage encrypt(Message message) {
|
private XmppAxolotlMessage buildHeader(Contact contact) {
|
||||||
final String content;
|
final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(
|
||||||
if (message.hasFileOnRemoteHost()) {
|
contact.getJid().toBareJid(), getOwnDeviceId());
|
||||||
content = message.getFileParams().url.toString();
|
|
||||||
} else {
|
|
||||||
content = message.getBody();
|
|
||||||
}
|
|
||||||
final XmppAxolotlMessage axolotlMessage;
|
|
||||||
try {
|
|
||||||
axolotlMessage = new XmppAxolotlMessage(message.getContact().getJid().toBareJid(),
|
|
||||||
getOwnDeviceId(), content);
|
|
||||||
} catch (CryptoFailedException e) {
|
|
||||||
Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to encrypt message: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (findSessionsforContact(message.getContact()).isEmpty()) {
|
Set<XmppAxolotlSession> contactSessions = findSessionsforContact(contact);
|
||||||
|
Set<XmppAxolotlSession> ownSessions = findOwnSessions();
|
||||||
|
if (contactSessions.isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl foreign keyElements...");
|
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl foreign keyElements...");
|
||||||
for (XmppAxolotlSession session : findSessionsforContact(message.getContact())) {
|
for (XmppAxolotlSession session : contactSessions) {
|
||||||
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
|
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
|
||||||
axolotlMessage.addKeyElement(session.processSending(axolotlMessage.getInnerKey()));
|
axolotlMessage.addDevice(session);
|
||||||
}
|
}
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl own keyElements...");
|
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Building axolotl own keyElements...");
|
||||||
for (XmppAxolotlSession session : findOwnSessions()) {
|
for (XmppAxolotlSession session : ownSessions) {
|
||||||
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
|
Log.v(Config.LOGTAG, AxolotlService.getLogprefix(account) + session.getRemoteAddress().toString());
|
||||||
axolotlMessage.addKeyElement(session.processSending(axolotlMessage.getInnerKey()));
|
axolotlMessage.addDevice(session);
|
||||||
}
|
}
|
||||||
|
|
||||||
return axolotlMessage;
|
return axolotlMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void processSending(final Message message, final boolean delay) {
|
@Nullable
|
||||||
|
public XmppAxolotlMessage encrypt(Message message) {
|
||||||
|
XmppAxolotlMessage axolotlMessage = buildHeader(message.getContact());
|
||||||
|
|
||||||
|
if (axolotlMessage != null) {
|
||||||
|
final String content;
|
||||||
|
if (message.hasFileOnRemoteHost()) {
|
||||||
|
content = message.getFileParams().url.toString();
|
||||||
|
} else {
|
||||||
|
content = message.getBody();
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
axolotlMessage.encrypt(content);
|
||||||
|
} catch (CryptoFailedException e) {
|
||||||
|
Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to encrypt message: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return axolotlMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void preparePayloadMessage(final Message message, final boolean delay) {
|
||||||
executor.execute(new Runnable() {
|
executor.execute(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -615,13 +620,14 @@ public class AxolotlService {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public void prepareMessage(final Message message, final boolean delay) {
|
public void prepareKeyTransportMessage(final Contact contact, final OnMessageCreatedCallback onMessageCreatedCallback) {
|
||||||
if (!messageCache.containsKey(message.getUuid())) {
|
executor.execute(new Runnable() {
|
||||||
boolean newSessions = createSessionsIfNeeded(message.getConversation(), true);
|
@Override
|
||||||
if (!newSessions) {
|
public void run() {
|
||||||
this.processSending(message, delay);
|
XmppAxolotlMessage axolotlMessage = buildHeader(contact);
|
||||||
|
onMessageCreatedCallback.run(axolotlMessage);
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) {
|
public XmppAxolotlMessage fetchAxolotlMessageFromCache(Message message) {
|
||||||
|
@ -653,26 +659,15 @@ public class AxolotlService {
|
||||||
newSession = true;
|
newSession = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (XmppAxolotlMessage.XmppAxolotlKeyElement keyElement : message.getKeyElements()) {
|
try {
|
||||||
if (keyElement.getRecipientDeviceId() == getOwnDeviceId()) {
|
plaintextMessage = message.decrypt(session, getOwnDeviceId());
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Found axolotl keyElement matching own device ID, processing...");
|
Integer preKeyId = session.getPreKeyId();
|
||||||
byte[] payloadKey = session.processReceiving(keyElement);
|
if (preKeyId != null) {
|
||||||
if (payloadKey != null) {
|
publishBundlesIfNeeded();
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Got payload key from axolotl keyElement. Decrypting message...");
|
session.resetPreKeyId();
|
||||||
try {
|
|
||||||
plaintextMessage = message.decrypt(session, payloadKey, session.getFingerprint());
|
|
||||||
} catch (CryptoFailedException e) {
|
|
||||||
Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message: " + e.getMessage());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Integer preKeyId = session.getPreKeyId();
|
|
||||||
if (preKeyId != null) {
|
|
||||||
publishBundlesIfNeeded();
|
|
||||||
session.resetPreKeyId();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} catch (CryptoFailedException e) {
|
||||||
|
Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message: " + e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (newSession && plaintextMessage != null) {
|
if (newSession && plaintextMessage != null) {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
package eu.siacs.conversations.crypto.axolotl;
|
package eu.siacs.conversations.crypto.axolotl;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.util.Base64;
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
@ -9,8 +8,9 @@ import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.HashSet;
|
import java.util.HashMap;
|
||||||
import java.util.Set;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
@ -26,9 +26,11 @@ 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 CONTAINERTAG = "encrypted";
|
||||||
public static final String HEADER = "header";
|
public static final String HEADER = "header";
|
||||||
public static final String SOURCEID = "sid";
|
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 IVTAG = "iv";
|
||||||
public static final String PAYLOAD = "payload";
|
public static final String PAYLOAD = "payload";
|
||||||
|
|
||||||
|
@ -39,54 +41,15 @@ public class XmppAxolotlMessage {
|
||||||
private byte[] innerKey;
|
private byte[] innerKey;
|
||||||
private byte[] ciphertext = null;
|
private byte[] ciphertext = null;
|
||||||
private byte[] iv = null;
|
private byte[] iv = null;
|
||||||
private final Set<XmppAxolotlKeyElement> keyElements;
|
private final Map<Integer, byte[]> keys;
|
||||||
private final Jid from;
|
private final Jid from;
|
||||||
private final int sourceDeviceId;
|
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 {
|
public static class XmppAxolotlPlaintextMessage {
|
||||||
private final XmppAxolotlSession session;
|
|
||||||
private final String plaintext;
|
private final String plaintext;
|
||||||
private final String fingerprint;
|
private final String fingerprint;
|
||||||
|
|
||||||
public XmppAxolotlPlaintextMessage(XmppAxolotlSession session, String plaintext, String fingerprint) {
|
public XmppAxolotlPlaintextMessage(String plaintext, String fingerprint) {
|
||||||
this.session = session;
|
|
||||||
this.plaintext = plaintext;
|
this.plaintext = plaintext;
|
||||||
this.fingerprint = fingerprint;
|
this.fingerprint = fingerprint;
|
||||||
}
|
}
|
||||||
|
@ -95,24 +58,28 @@ public class XmppAxolotlMessage {
|
||||||
return plaintext;
|
return plaintext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmppAxolotlSession getSession() {
|
|
||||||
return session;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFingerprint() {
|
public String getFingerprint() {
|
||||||
return fingerprint;
|
return fingerprint;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmppAxolotlMessage(Jid from, Element axolotlMessage) throws IllegalArgumentException {
|
private XmppAxolotlMessage(final Element axolotlMessage, final Jid from) throws IllegalArgumentException {
|
||||||
this.from = from;
|
this.from = from;
|
||||||
Element header = axolotlMessage.findChild(HEADER);
|
Element header = axolotlMessage.findChild(HEADER);
|
||||||
this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
|
this.sourceDeviceId = Integer.parseInt(header.getAttribute(SOURCEID));
|
||||||
this.keyElements = new HashSet<>();
|
List<Element> keyElements = header.getChildren();
|
||||||
for (Element keyElement : header.getChildren()) {
|
this.keys = new HashMap<>(keyElements.size());
|
||||||
|
for (Element keyElement : keyElements) {
|
||||||
switch (keyElement.getName()) {
|
switch (keyElement.getName()) {
|
||||||
case XmppAxolotlKeyElement.TAGNAME:
|
case KEYTAG:
|
||||||
keyElements.add(new XmppAxolotlKeyElement(keyElement));
|
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;
|
break;
|
||||||
case IVTAG:
|
case IVTAG:
|
||||||
if (this.iv != null) {
|
if (this.iv != null) {
|
||||||
|
@ -134,14 +101,13 @@ public class XmppAxolotlMessage {
|
||||||
public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
|
public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
|
||||||
this.from = from;
|
this.from = from;
|
||||||
this.sourceDeviceId = sourceDeviceId;
|
this.sourceDeviceId = sourceDeviceId;
|
||||||
this.keyElements = new HashSet<>();
|
this.keys = new HashMap<>();
|
||||||
this.iv = generateIv();
|
this.iv = generateIv();
|
||||||
this.innerKey = generateKey();
|
this.innerKey = generateKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) throws CryptoFailedException {
|
public static XmppAxolotlMessage fromElement(Element element, Jid from) {
|
||||||
this(from, sourceDeviceId);
|
return new XmppAxolotlMessage(element, from);
|
||||||
this.encrypt(plaintext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] generateKey() {
|
private static byte[] generateKey() {
|
||||||
|
@ -162,7 +128,7 @@ public class XmppAxolotlMessage {
|
||||||
return iv;
|
return iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encrypt(String plaintext) throws CryptoFailedException {
|
public void encrypt(String plaintext) throws CryptoFailedException {
|
||||||
try {
|
try {
|
||||||
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
|
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
|
@ -189,13 +155,10 @@ public class XmppAxolotlMessage {
|
||||||
return ciphertext;
|
return ciphertext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<XmppAxolotlKeyElement> getKeyElements() {
|
public void addDevice(XmppAxolotlSession session) {
|
||||||
return keyElements;
|
byte[] key = session.processSending(innerKey);
|
||||||
}
|
if (key != null) {
|
||||||
|
keys.put(session.getRemoteAddress().getDeviceId(), key);
|
||||||
public void addKeyElement(@Nullable XmppAxolotlKeyElement keyElement) {
|
|
||||||
if (keyElement != null) {
|
|
||||||
keyElements.add(keyElement);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -207,12 +170,15 @@ public class XmppAxolotlMessage {
|
||||||
return this.iv;
|
return this.iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Element toXml() {
|
public Element toElement() {
|
||||||
Element encryptionElement = new Element(TAGNAME, AxolotlService.PEP_PREFIX);
|
Element encryptionElement = new Element(CONTAINERTAG, AxolotlService.PEP_PREFIX);
|
||||||
Element headerElement = encryptionElement.addChild(HEADER);
|
Element headerElement = encryptionElement.addChild(HEADER);
|
||||||
headerElement.setAttribute(SOURCEID, sourceDeviceId);
|
headerElement.setAttribute(SOURCEID, sourceDeviceId);
|
||||||
for (XmppAxolotlKeyElement header : keyElements) {
|
for (Map.Entry<Integer, byte[]> keyEntry : keys.entrySet()) {
|
||||||
headerElement.addChild(header.toXml());
|
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));
|
headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.DEFAULT));
|
||||||
if (ciphertext != null) {
|
if (ciphertext != null) {
|
||||||
|
@ -222,24 +188,30 @@ public class XmppAxolotlMessage {
|
||||||
return encryptionElement;
|
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;
|
XmppAxolotlPlaintextMessage plaintextMessage = null;
|
||||||
try {
|
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 cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
|
cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
|
||||||
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());
|
||||||
|
|
||||||
String plaintext = new String(cipher.doFinal(ciphertext));
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
||||||
plaintextMessage = new XmppAxolotlPlaintextMessage(session, plaintext, fingerprint);
|
| InvalidAlgorithmParameterException | IllegalBlockSizeException
|
||||||
|
| BadPaddingException | NoSuchProviderException e) {
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
throw new CryptoFailedException(e);
|
||||||
| InvalidAlgorithmParameterException | IllegalBlockSizeException
|
}
|
||||||
| BadPaddingException | NoSuchProviderException e) {
|
|
||||||
throw new CryptoFailedException(e);
|
|
||||||
}
|
}
|
||||||
return plaintextMessage;
|
return plaintextMessage;
|
||||||
}
|
}
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class XmppAxolotlSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public byte[] processReceiving(XmppAxolotlMessage.XmppAxolotlKeyElement incomingHeader) {
|
public byte[] processReceiving(byte[] encryptedKey) {
|
||||||
byte[] plaintext = null;
|
byte[] plaintext = null;
|
||||||
SQLiteAxolotlStore.Trust trust = getTrust();
|
SQLiteAxolotlStore.Trust trust = getTrust();
|
||||||
switch (trust) {
|
switch (trust) {
|
||||||
|
@ -79,7 +79,7 @@ public class XmppAxolotlSession {
|
||||||
case TRUSTED:
|
case TRUSTED:
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
PreKeyWhisperMessage message = new PreKeyWhisperMessage(incomingHeader.getContents());
|
PreKeyWhisperMessage message = new PreKeyWhisperMessage(encryptedKey);
|
||||||
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
|
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "PreKeyWhisperMessage received, new session ID:" + message.getSignedPreKeyId() + "/" + message.getPreKeyId());
|
||||||
String fingerprint = message.getIdentityKey().getFingerprint().replaceAll("\\s", "");
|
String fingerprint = message.getIdentityKey().getFingerprint().replaceAll("\\s", "");
|
||||||
if (this.fingerprint != null && !this.fingerprint.equals(fingerprint)) {
|
if (this.fingerprint != null && !this.fingerprint.equals(fingerprint)) {
|
||||||
|
@ -93,7 +93,7 @@ public class XmppAxolotlSession {
|
||||||
}
|
}
|
||||||
} catch (InvalidMessageException | InvalidVersionException e) {
|
} catch (InvalidMessageException | InvalidVersionException e) {
|
||||||
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
|
Log.i(Config.LOGTAG, AxolotlService.getLogprefix(account) + "WhisperMessage received");
|
||||||
WhisperMessage message = new WhisperMessage(incomingHeader.getContents());
|
WhisperMessage message = new WhisperMessage(encryptedKey);
|
||||||
plaintext = cipher.decrypt(message);
|
plaintext = cipher.decrypt(message);
|
||||||
} catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
|
} catch (InvalidKeyException | InvalidKeyIdException | UntrustedIdentityException e) {
|
||||||
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
|
Log.w(Config.LOGTAG, AxolotlService.getLogprefix(account) + "Error decrypting axolotl header, " + e.getClass().getName() + ": " + e.getMessage());
|
||||||
|
@ -117,14 +117,11 @@ public class XmppAxolotlSession {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public XmppAxolotlMessage.XmppAxolotlKeyElement processSending(@NonNull byte[] outgoingMessage) {
|
public byte[] 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.XmppAxolotlKeyElement header =
|
return ciphertextMessage.serialize();
|
||||||
new XmppAxolotlMessage.XmppAxolotlKeyElement(remoteAddress.getDeviceId(),
|
|
||||||
ciphertextMessage.serialize());
|
|
||||||
return header;
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,7 +63,7 @@ public class MessageGenerator extends AbstractGenerator {
|
||||||
if (axolotlMessage == null) {
|
if (axolotlMessage == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
packet.setAxolotlMessage(axolotlMessage.toXml());
|
packet.setAxolotlMessage(axolotlMessage.toElement());
|
||||||
return packet;
|
return packet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ public class MessageParser extends AbstractParser implements
|
||||||
private Message parseAxolotlChat(Element axolotlMessage, Jid from, String id, Conversation conversation, int status) {
|
private Message parseAxolotlChat(Element axolotlMessage, Jid from, String id, Conversation conversation, int status) {
|
||||||
Message finishedMessage = null;
|
Message finishedMessage = null;
|
||||||
AxolotlService service = conversation.getAccount().getAxolotlService();
|
AxolotlService service = conversation.getAccount().getAxolotlService();
|
||||||
XmppAxolotlMessage xmppAxolotlMessage = new XmppAxolotlMessage(from.toBareJid(), axolotlMessage);
|
XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid());
|
||||||
XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceiving(xmppAxolotlMessage);
|
XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceiving(xmppAxolotlMessage);
|
||||||
if(plaintextMessage != null) {
|
if(plaintextMessage != null) {
|
||||||
finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
|
finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
|
||||||
|
@ -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(XmppAxolotlMessage.TAGNAME, AxolotlService.PEP_PREFIX);
|
final Element axolotlEncrypted = packet.findChild(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
|
||||||
int status;
|
int status;
|
||||||
final Jid counterpart;
|
final Jid counterpart;
|
||||||
final Jid to = packet.getTo();
|
final Jid to = packet.getTo();
|
||||||
|
|
|
@ -764,7 +764,7 @@ public class XmppConnectionService extends Service implements OnPhoneContactsLoa
|
||||||
} else {
|
} else {
|
||||||
XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message);
|
XmppAxolotlMessage axolotlMessage = account.getAxolotlService().fetchAxolotlMessageFromCache(message);
|
||||||
if (axolotlMessage == null) {
|
if (axolotlMessage == null) {
|
||||||
account.getAxolotlService().prepareMessage(message,delay);
|
account.getAxolotlService().preparePayloadMessage(message, delay);
|
||||||
message.setAxolotlFingerprint(account.getAxolotlService().getOwnPublicKey().getFingerprint().replaceAll("\\s", ""));
|
message.setAxolotlFingerprint(account.getAxolotlService().getOwnPublicKey().getFingerprint().replaceAll("\\s", ""));
|
||||||
} else {
|
} else {
|
||||||
packet = mMessageGenerator.generateAxolotlChat(message, axolotlMessage);
|
packet = mMessageGenerator.generateAxolotlChat(message, axolotlMessage);
|
||||||
|
|
|
@ -1265,7 +1265,7 @@ public class ConversationActivity extends XmppActivity
|
||||||
|| !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty();
|
|| !axolotlService.findDevicesWithoutSession(mSelectedConversation).isEmpty();
|
||||||
boolean hasNoTrustedKeys = axolotlService.getNumTrustedKeys(mSelectedConversation.getContact()) == 0;
|
boolean hasNoTrustedKeys = axolotlService.getNumTrustedKeys(mSelectedConversation.getContact()) == 0;
|
||||||
if( hasPendingKeys || hasNoTrustedKeys) {
|
if( hasPendingKeys || hasNoTrustedKeys) {
|
||||||
axolotlService.createSessionsIfNeeded(mSelectedConversation, false);
|
axolotlService.createSessionsIfNeeded(mSelectedConversation);
|
||||||
Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class);
|
Intent intent = new Intent(getApplicationContext(), TrustKeysActivity.class);
|
||||||
intent.putExtra("contact", mSelectedConversation.getContact().getJid().toBareJid().toString());
|
intent.putExtra("contact", mSelectedConversation.getContact().getJid().toBareJid().toString());
|
||||||
intent.putExtra("account", mSelectedConversation.getAccount().getJid().toBareJid().toString());
|
intent.putExtra("account", mSelectedConversation.getAccount().getJid().toBareJid().toString());
|
||||||
|
|
Loading…
Reference in New Issue