make sure omemo sessions are verified if the the respective config flag is set

Daniel Gultsch created

Change summary

src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java   | 24 
src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java | 21 
src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java      | 10 
3 files changed, 46 insertions(+), 9 deletions(-)

Detailed changes

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

@@ -1234,6 +1234,9 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
         if (session == null) {
             throw new CryptoFailedException(String.format("No session found for %d", deviceId));
         }
+        if (Config.REQUIRE_RTP_VERIFICATION) {
+            requireVerification(session);
+        }
         final ImmutableMap.Builder<String, RtpContentMap.DescriptionTransport> descriptionTransportBuilder = new ImmutableMap.Builder<>();
         final OmemoVerification omemoVerification = new OmemoVerification();
         omemoVerification.setDeviceId(deviceId);
@@ -1283,6 +1286,9 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
                 final Element encrypted = child.findChildEnsureSingle(XmppAxolotlMessage.CONTAINERTAG, AxolotlService.PEP_PREFIX);
                 final XmppAxolotlMessage xmppAxolotlMessage = XmppAxolotlMessage.fromElement(encrypted, from.asBareJid());
                 final XmppAxolotlSession session = getReceivingSession(xmppAxolotlMessage);
+                if (Config.REQUIRE_RTP_VERIFICATION) {
+                    requireVerification(session);
+                }
                 final XmppAxolotlMessage.XmppAxolotlPlaintextMessage plaintext = xmppAxolotlMessage.decrypt(session, getOwnDeviceId());
                 final Integer preKeyId = session.getPreKeyIdAndReset();
                 if (preKeyId != null) {
@@ -1299,6 +1305,16 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
         return new OmemoVerifiedPayload<>(omemoVerification, transportInfo);
     }
 
+    private static void requireVerification(final XmppAxolotlSession session) {
+        if (session.getTrust().isVerified()) {
+            return;
+        }
+        throw new NotVerifiedException(String.format(
+                "session with %s was not verified",
+                session.getFingerprint()
+        ));
+    }
+
     public void prepareKeyTransportMessage(final Conversation conversation, final OnMessageCreatedCallback onMessageCreatedCallback) {
         executor.execute(new Runnable() {
             @Override
@@ -1690,4 +1706,12 @@ public class AxolotlService implements OnAdvancedStreamFeaturesLoaded {
             return payload;
         }
     }
+
+    public static class NotVerifiedException extends SecurityException {
+
+        public NotVerifiedException(String message) {
+            super(message);
+        }
+
+    }
 }

src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java 🔗

@@ -762,9 +762,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         } catch (final WebRTCWrapper.InitializationException e) {
             Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to initialize WebRTC");
             webRTCWrapper.close();
-            sendJingleMessage("retract", id.with.asBareJid());
-            transitionOrThrow(State.TERMINATED_APPLICATION_FAILURE);
-            this.finish();
+            sendRetract(Reason.ofException(e));
             return;
         }
         try {
@@ -776,22 +774,27 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web
         } catch (final Exception e) {
             Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": unable to sendSessionInitiate", Throwables.getRootCause(e));
             webRTCWrapper.close();
+            final Reason reason = Reason.ofException(e);
             if (isInState(targetState)) {
-                sendSessionTerminate(Reason.FAILED_APPLICATION);
+                sendSessionTerminate(reason);
             } else {
-                sendJingleMessage("retract", id.with.asBareJid());
-                transitionOrThrow(State.TERMINATED_APPLICATION_FAILURE);
-                this.finish();
+                sendRetract(reason);
             }
         }
     }
 
+    private void sendRetract(final Reason reason) {
+        //TODO embed reason into retract
+        sendJingleMessage("retract", id.with.asBareJid());
+        transitionOrThrow(reasonToState(reason));
+        this.finish();
+    }
+
     private void sendSessionInitiate(final RtpContentMap rtpContentMap, final State targetState) {
         this.initiatorRtpContentMap = rtpContentMap;
-        this.transitionOrThrow(targetState);
-        //TODO do on background thread?
         final RtpContentMap outgoingContentMap = encryptSessionInitiate(rtpContentMap);
         final JinglePacket sessionInitiate = outgoingContentMap.toJinglePacket(JinglePacket.Action.SESSION_INITIATE, id.sessionId);
+        this.transitionOrThrow(targetState);
         send(sessionInitiate);
     }
 

src/main/java/eu/siacs/conversations/xmpp/jingle/stanzas/Reason.java 🔗

@@ -3,7 +3,9 @@ package eu.siacs.conversations.xmpp.jingle.stanzas;
 import androidx.annotation.NonNull;
 
 import com.google.common.base.CaseFormat;
+import com.google.common.base.Throwables;
 
+import eu.siacs.conversations.crypto.axolotl.AxolotlService;
 import eu.siacs.conversations.xmpp.jingle.RtpContentMap;
 
 public enum Reason {
@@ -51,4 +53,12 @@ public enum Reason {
             return FAILED_APPLICATION;
         }
     }
+
+    public static Reason ofException(final Exception e) {
+        final Throwable root = Throwables.getRootCause(e);
+        if (root instanceof RuntimeException) {
+            return of((RuntimeException) root);
+        }
+        return FAILED_APPLICATION;
+    }
 }