treat omemo keys >= 32 bytes as containing auth tag. add config flag to put auth tag in key

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/Config.java                            |  1 
src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java     | 47 
src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java | 31 
3 files changed, 54 insertions(+), 25 deletions(-)

Detailed changes

src/main/java/eu/siacs/conversations/Config.java 🔗

@@ -83,6 +83,7 @@ public final class Config {
 	public static final long OMEMO_AUTO_EXPIRY = 7 * MILLISECONDS_IN_DAY;
 	public static final boolean REMOVE_BROKEN_DEVICES = false;
 	public static final boolean OMEMO_PADDING = false;
+	public static boolean PUT_AUTH_TAG_INTO_KEY = false;
 
 
 	public static final boolean DISABLE_PROXY_LOOKUP = false; //useful to debug ibb

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java 🔗

@@ -1007,14 +1007,11 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
 	}
 
 	@Nullable
-	private XmppAxolotlMessage buildHeader(Conversation conversation) {
-		final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(
-				account.getJid().toBareJid(), getOwnDeviceId());
-
+	private boolean buildHeader(XmppAxolotlMessage axolotlMessage, Conversation conversation) {
 		Set<XmppAxolotlSession> remoteSessions = findSessionsForConversation(conversation);
 		Collection<XmppAxolotlSession> ownSessions = findOwnSessions();
 		if (remoteSessions.isEmpty()) {
-			return null;
+			return false;
 		}
 		for (XmppAxolotlSession session : remoteSessions) {
 			axolotlMessage.addDevice(session);
@@ -1023,26 +1020,26 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
 			axolotlMessage.addDevice(session);
 		}
 
-		return axolotlMessage;
+		return true;
 	}
 
 	@Nullable
 	public XmppAxolotlMessage encrypt(Message message) {
-		XmppAxolotlMessage axolotlMessage = buildHeader(message.getConversation());
-
-		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;
-			}
+		final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(account.getJid().toBareJid(), getOwnDeviceId());
+		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;
+		}
+		if (!buildHeader(axolotlMessage,message.getConversation())) {
+			return null;
 		}
 
 		return axolotlMessage;
@@ -1069,8 +1066,12 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
 		executor.execute(new Runnable() {
 			@Override
 			public void run() {
-				XmppAxolotlMessage axolotlMessage = buildHeader(conversation);
-				onMessageCreatedCallback.run(axolotlMessage);
+				final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(account.getJid().toBareJid(), getOwnDeviceId());
+				if (buildHeader(axolotlMessage,conversation)) {
+					onMessageCreatedCallback.run(axolotlMessage);
+				} else {
+					onMessageCreatedCallback.run(null);
+				}
 			}
 		});
 	}

src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java 🔗

@@ -3,7 +3,6 @@ package eu.siacs.conversations.crypto.axolotl;
 import android.util.Base64;
 import android.util.Log;
 
-import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
 
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -24,6 +23,7 @@ import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 import eu.siacs.conversations.Config;
+import eu.siacs.conversations.utils.CryptoHelper;
 import eu.siacs.conversations.xml.Element;
 import eu.siacs.conversations.xmpp.jid.Jid;
 
@@ -42,6 +42,7 @@ public class XmppAxolotlMessage {
 
 	private byte[] innerKey;
 	private byte[] ciphertext = null;
+	private byte[] authtagPlusInnerKey = null;
 	private byte[] iv = null;
 	private final Map<Integer, XmppAxolotlSession.AxolotlKey> keys;
 	private final Jid from;
@@ -166,6 +167,14 @@ public class XmppAxolotlMessage {
 			Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
 			cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
 			this.ciphertext = cipher.doFinal(Config.OMEMO_PADDING ? getPaddedBytes(plaintext) : plaintext.getBytes());
+			if (Config.PUT_AUTH_TAG_INTO_KEY && this.ciphertext != null) {
+				this.authtagPlusInnerKey = new byte[16+16];
+				byte[] ciphertext = new byte[this.ciphertext.length - 16];
+				System.arraycopy(this.ciphertext,0,ciphertext,0,ciphertext.length);
+				System.arraycopy(this.ciphertext,ciphertext.length,authtagPlusInnerKey,16,16);
+				System.arraycopy(this.innerKey,0,authtagPlusInnerKey,0,this.innerKey.length);
+				this.ciphertext = ciphertext;
+			}
 		} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
 				| IllegalBlockSizeException | BadPaddingException | NoSuchProviderException
 				| InvalidAlgorithmParameterException e) {
@@ -202,7 +211,12 @@ public class XmppAxolotlMessage {
 	}
 
 	public void addDevice(XmppAxolotlSession session) {
-		XmppAxolotlSession.AxolotlKey key = session.processSending(innerKey);
+		XmppAxolotlSession.AxolotlKey key;
+		if (authtagPlusInnerKey != null) {
+			key = session.processSending(authtagPlusInnerKey);
+		} else {
+			key = session.processSending(innerKey);
+		}
 		if (key != null) {
 			keys.put(session.getRemoteAddress().getDeviceId(), key);
 		}
@@ -254,6 +268,19 @@ 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");
+					byte[] newCipherText = new byte[key.length - 16  + ciphertext.length];
+					byte[] newKey = new byte[16];
+					System.arraycopy(ciphertext, 0, newCipherText, 0, ciphertext.length);
+					System.arraycopy(key, 16, newCipherText, ciphertext.length, authtaglength);
+					System.arraycopy(key,0,newKey,0,newKey.length);
+					ciphertext = newCipherText;
+					key = newKey;
+				}
+
 				Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
 				SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
 				IvParameterSpec ivSpec = new IvParameterSpec(iv);