properly handle key transport messages. use prekeyparsing only when that attribute is set

This commit is contained in:
Daniel Gultsch 2018-01-18 20:58:55 +01:00
parent 6663eb0a12
commit 036dd82698
5 changed files with 54 additions and 47 deletions

View file

@ -1313,16 +1313,15 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return session;
}
public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message) {
public XmppAxolotlMessage.XmppAxolotlPlaintextMessage processReceivingPayloadMessage(XmppAxolotlMessage message, boolean postponePreKeyMessageHandling) {
XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = null;
XmppAxolotlSession session = getReceivingSession(message);
try {
plaintextMessage = message.decrypt(session, getOwnDeviceId());
Integer preKeyId = session.getPreKeyId();
Integer preKeyId = session.getPreKeyIdAndReset();
if (preKeyId != null) {
publishBundlesIfNeeded(false, false);
session.resetPreKeyId();
postPreKeyMessageHandling(session, preKeyId, postponePreKeyMessageHandling);
}
} catch (CryptoFailedException e) {
Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message from "+message.getFrom()+": " + e.getMessage());
@ -1335,12 +1334,22 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
return plaintextMessage;
}
public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message) {
private void postPreKeyMessageHandling(final XmppAxolotlSession session, int preKeyId, final boolean postpone) {
Log.d(Config.LOGTAG,account.getJid().toBareJid()+": postPreKeyMessageHandling() preKeyId="+preKeyId+", postpone="+Boolean.toString(postpone));
//TODO: do not republish if we already removed this preKeyId
publishBundlesIfNeeded(false, false);
}
public XmppAxolotlMessage.XmppAxolotlKeyTransportMessage processReceivingKeyTransportMessage(XmppAxolotlMessage message, final boolean postponePreKeyMessageHandling) {
XmppAxolotlMessage.XmppAxolotlKeyTransportMessage keyTransportMessage;
XmppAxolotlSession session = getReceivingSession(message);
try {
keyTransportMessage = message.getParameters(session, getOwnDeviceId());
Integer preKeyId = session.getPreKeyIdAndReset();
if (preKeyId != null) {
postPreKeyMessageHandling(session, preKeyId, postponePreKeyMessageHandling);
}
} catch (CryptoFailedException e) {
Log.d(Config.LOGTAG,"could not decrypt keyTransport message "+e.getMessage());
keyTransportMessage = null;

View file

@ -2,6 +2,7 @@ package eu.siacs.conversations.crypto.axolotl;
import android.util.Base64;
import android.util.Log;
import android.util.SparseArray;
import java.security.InvalidAlgorithmParameterException;
@ -43,7 +44,7 @@ public class XmppAxolotlMessage {
private byte[] ciphertext = null;
private byte[] authtagPlusInnerKey = null;
private byte[] iv = null;
private final Map<Integer, XmppAxolotlSession.AxolotlKey> keys;
private final SparseArray<XmppAxolotlSession.AxolotlKey> keys;
private final Jid from;
private final int sourceDeviceId;
@ -99,7 +100,7 @@ public class XmppAxolotlMessage {
throw new IllegalArgumentException("invalid source id");
}
List<Element> keyElements = header.getChildren();
this.keys = new HashMap<>(keyElements.size());
this.keys = new SparseArray<>();
for (Element keyElement : keyElements) {
switch (keyElement.getName()) {
case KEYTAG:
@ -132,7 +133,7 @@ public class XmppAxolotlMessage {
public XmppAxolotlMessage(Jid from, int sourceDeviceId) {
this.from = from;
this.sourceDeviceId = sourceDeviceId;
this.keys = new HashMap<>();
this.keys = new SparseArray<>();
this.iv = generateIv();
this.innerKey = generateKey();
}
@ -159,6 +160,10 @@ public class XmppAxolotlMessage {
return iv;
}
public boolean hasPayload() {
return ciphertext != null;
}
public void encrypt(String plaintext) throws CryptoFailedException {
try {
SecretKey secretKey = new SecretKeySpec(innerKey, KEYTYPE);
@ -205,10 +210,6 @@ public class XmppAxolotlMessage {
return sourceDeviceId;
}
public byte[] getCiphertext() {
return ciphertext;
}
public void addDevice(XmppAxolotlSession session) {
XmppAxolotlSession.AxolotlKey key;
if (authtagPlusInnerKey != null) {
@ -233,13 +234,13 @@ public class XmppAxolotlMessage {
Element encryptionElement = new Element(CONTAINERTAG, AxolotlService.PEP_PREFIX);
Element headerElement = encryptionElement.addChild(HEADER);
headerElement.setAttribute(SOURCEID, sourceDeviceId);
for (Map.Entry<Integer, XmppAxolotlSession.AxolotlKey> keyEntry : keys.entrySet()) {
for(int i = 0; i < keys.size(); ++i) {
Element keyElement = new Element(KEYTAG);
keyElement.setAttribute(REMOTEID, keyEntry.getKey());
if (keyEntry.getValue().prekey) {
keyElement.setAttribute(REMOTEID, keys.keyAt(i));
if (keys.valueAt(i).prekey) {
keyElement.setAttribute("prekey","true");
}
keyElement.setContent(Base64.encodeToString(keyEntry.getValue().key, Base64.NO_WRAP));
keyElement.setContent(Base64.encodeToString(keys.valueAt(i).key, Base64.NO_WRAP));
headerElement.addChild(keyElement);
}
headerElement.addChild(IVTAG).setContent(Base64.encodeToString(iv, Base64.NO_WRAP));
@ -267,7 +268,6 @@ public class XmppAxolotlMessage {
byte[] key = unpackKey(session, sourceDeviceId);
if (key != null) {
try {
if (key.length >= 32) {
int authtaglength = key.length - 16;
Log.d(Config.LOGTAG,"found auth tag as part of omemo key");

View file

@ -43,15 +43,12 @@ public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> {
this.account = account;
}
public Integer getPreKeyId() {
public Integer getPreKeyIdAndReset() {
final Integer preKeyId = this.preKeyId;
this.preKeyId = null;
return preKeyId;
}
public void resetPreKeyId() {
preKeyId = null;
}
public String getFingerprint() {
return identityKey == null ? null : CryptoHelper.bytesToHex(identityKey.getPublicKey().serialize());
}
@ -87,11 +84,10 @@ public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> {
FingerprintStatus status = getTrust();
if (!status.isCompromised()) {
try {
CiphertextMessage ciphertextMessage;
try {
ciphertextMessage = new PreKeySignalMessage(encryptedKey.key);
Optional<Integer> optionalPreKeyId = ((PreKeySignalMessage) ciphertextMessage).getPreKeyId();
IdentityKey identityKey = ((PreKeySignalMessage) ciphertextMessage).getIdentityKey();
if (encryptedKey.prekey) {
PreKeySignalMessage preKeySignalMessage = new PreKeySignalMessage(encryptedKey.key);
Optional<Integer> optionalPreKeyId = preKeySignalMessage.getPreKeyId();
IdentityKey identityKey = preKeySignalMessage.getIdentityKey();
if (!optionalPreKeyId.isPresent()) {
throw new CryptoFailedException("PreKeyWhisperMessage did not contain a PreKeyId");
}
@ -100,15 +96,13 @@ public class XmppAxolotlSession implements Comparable<XmppAxolotlSession> {
throw new CryptoFailedException("Received PreKeyWhisperMessage but preexisting identity key changed.");
}
this.identityKey = identityKey;
} catch (InvalidVersionException | InvalidMessageException e) {
ciphertextMessage = new SignalMessage(encryptedKey.key);
}
if (ciphertextMessage instanceof PreKeySignalMessage) {
plaintext = cipher.decrypt((PreKeySignalMessage) ciphertextMessage);
plaintext = cipher.decrypt(preKeySignalMessage);
} else {
plaintext = cipher.decrypt((SignalMessage) ciphertextMessage);
SignalMessage signalMessage = new SignalMessage(encryptedKey.key);
plaintext = cipher.decrypt(signalMessage);
preKeyId = null; //better safe than sorry because we use that to do special after prekey handling
}
} catch (InvalidKeyException | LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException | InvalidKeyIdException | UntrustedIdentityException e) {
} catch (InvalidVersionException | InvalidKeyException | LegacyMessageException | InvalidMessageException | DuplicateMessageException | NoSessionException | InvalidKeyIdException | UntrustedIdentityException e) {
if (!(e instanceof DuplicateMessageException)) {
e.printStackTrace();
}

View file

@ -163,24 +163,28 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
return false;
}
private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status) {
AxolotlService service = conversation.getAccount().getAxolotlService();
XmppAxolotlMessage xmppAxolotlMessage;
private Message parseAxolotlChat(Element axolotlMessage, Jid from, Conversation conversation, int status, boolean postpone) {
final AxolotlService service = conversation.getAccount().getAxolotlService();
final XmppAxolotlMessage xmppAxolotlMessage;
try {
xmppAxolotlMessage = XmppAxolotlMessage.fromElement(axolotlMessage, from.toBareJid());
} catch (Exception e) {
Log.d(Config.LOGTAG, conversation.getAccount().getJid().toBareJid() + ": invalid omemo message received " + e.getMessage());
return null;
}
XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage);
if (xmppAxolotlMessage.hasPayload()) {
final XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintextMessage = service.processReceivingPayloadMessage(xmppAxolotlMessage, postpone);
if (plaintextMessage != null) {
Message finishedMessage = new Message(conversation, plaintextMessage.getPlaintext(), Message.ENCRYPTION_AXOLOTL, status);
finishedMessage.setFingerprint(plaintextMessage.getFingerprint());
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(finishedMessage.getConversation().getAccount()) + " Received Message with session fingerprint: " + plaintextMessage.getFingerprint());
return finishedMessage;
} else {
return null;
}
} else {
Log.d(Config.LOGTAG,conversation.getAccount().getJid().toBareJid()+": received OMEMO key transport message");
service.processReceivingKeyTransportMessage(xmppAxolotlMessage, postpone);
}
return null;
}
private class Invite {
@ -468,7 +472,7 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece
} else {
origin = from;
}
message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status);
message = parseAxolotlChat(axolotlEncrypted, origin, conversation, status, query != null);
if (message == null) {
if (query == null && extractChatState(mXmppConnectionService.find(account, counterpart.toBareJid()), isTypeGroupChat, packet)) {
mXmppConnectionService.updateConversationUi();

View file

@ -448,7 +448,7 @@ public class JingleConnection implements Transferable {
}
this.file = this.mXmppConnectionService.getFileBackend().getFile(message, false);
if (mXmppAxolotlMessage != null) {
XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage);
XmppAxolotlMessage.XmppAxolotlKeyTransportMessage transportMessage = account.getAxolotlService().processReceivingKeyTransportMessage(mXmppAxolotlMessage, false);
if (transportMessage != null) {
message.setEncryption(Message.ENCRYPTION_AXOLOTL);
this.file.setKey(transportMessage.getKey());