Smack 4.4.0-alpha3: OMEMO raw session setup with new devices

When a new OMEMO chat session is first setup between two new devices A and B;
if device A sends an OMEMO message to device B, OmemoService on device B will throw NoRawSessionException, as there is no prior raw session being setup between the two devices.

From source, it seems that OmemoService just logged the error message, ignore/discard the newly received OMEMO message, but proceed to repairBrokenSessionWithPreKeyMessage(). However device B is unaware of the problem, and there is no display of the new received OMEMO message.

Prior to sending any OMEMO message, presumably neither Device A or Device B know each other OMEMEO device ID. I am unable to figure a way to build the raw session under this condition.

Does OmemoManager provides a method call to build the raw sessions for new devices before the messages are being sent/received; and what are the steps to do so.
Any alternatives to ensure all OMEMO Devices are able to display the very first OMEMO received.

03-25 08:02:32.004 19291-2870/org.atalk.android D/SMACK: RECV (1): 
    <message xml:lang='en-GB' to='leopard@atalk.sytes.net/atalk' from='chatroom-mfxm@conference.atalk.sytes.net/swordfish' type='groupchat' id='U3VAE-189'>
      <archived by='chatroom-mfxm@conference.atalk.sytes.net' id='1585094551076335' xmlns='urn:xmpp:mam:tmp'/>
      <stanza-id by='chatroom-mfxm@conference.atalk.sytes.net' id='1585094551076335' xmlns='urn:xmpp:sid:0'/>
      <encrypted xmlns='eu.siacs.conversations.axolotl'>
        <header sid='595475613'>
          <key rid='2022975859'>
            MwohBULW3HTjJIe4n3qtqZRo7nhqWWmZHI+H35n0Tq0LYqguEAEYASIw05iHCUeHN9coZNCAesweSsekJy1Yr5Fdofx2O4iUJ5Cqnz0CLnWhSqF+SIs8je90Y9IMAP/BPQo=
          </key>
          <iv>
            R30a/2e15C/fhg8+Ln+Eqw==
          </iv>
        </header>
        <payload>
          tKejCw==
        </payload>
      </encrypted>
      <store xmlns='urn:xmpp:hints'/>
      <encryption xmlns='urn:xmpp:eme:0' namespace='eu.siacs.conversations.axolotl' name='OMEMO'/>
      <active xmlns='http://jabber.org/protocol/chatstates'/>
      <body>
        I sent you an OMEMO encrypted message but your client doesn&apos;t seem to support that. Find more information on https://conversations.im/omemo
      </body>
    </message>
03-25 08:02:32.394 19291-3033/org.atalk.android W/aTalk: [8] org.jivesoftware.smackx.omemo.OmemoService.onOmemoMessageStanzaReceived() No raw session found for contact swordfish@atalk.sytes.net:595475613. 
    org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException: org.whispersystems.libsignal.InvalidMessageException: No valid sessions.
        at org.jivesoftware.smackx.omemo.signal.SignalOmemoRatchet.doubleRatchetDecrypt(SignalOmemoRatchet.java:128)
        at org.jivesoftware.smackx.omemo.OmemoRatchet.retrieveMessageKeyAndAuthTag(OmemoRatchet.java:106)
        at org.jivesoftware.smackx.omemo.OmemoService.decryptMessage(OmemoService.java:456)
        at org.jivesoftware.smackx.omemo.OmemoService.onOmemoMessageStanzaReceived(OmemoService.java:1166)
        at org.jivesoftware.smackx.omemo.OmemoManager$3$1.run(OmemoManager.java:945)
        at java.lang.Thread.run(Thread.java:818)
     Caused by: org.whispersystems.libsignal.InvalidMessageException: No valid sessions.
        at org.whispersystems.libsignal.SessionCipher.decrypt(SessionCipher.java:290)
        at org.whispersystems.libsignal.SessionCipher.decrypt(SessionCipher.java:243)
        at org.whispersystems.libsignal.SessionCipher.decrypt(SessionCipher.java:211)
        at org.jivesoftware.smackx.omemo.signal.SignalOmemoRatchet.doubleRatchetDecrypt(SignalOmemoRatchet.java:122)
        at org.jivesoftware.smackx.omemo.OmemoRatchet.retrieveMessageKeyAndAuthTag(OmemoRatchet.java:106) 
        at org.jivesoftware.smackx.omemo.OmemoService.decryptMessage(OmemoService.java:456) 
        at org.jivesoftware.smackx.omemo.OmemoService.onOmemoMessageStanzaReceived(OmemoService.java:1166) 
        at org.jivesoftware.smackx.omemo.OmemoManager$3$1.run(OmemoManager.java:945) 
        at java.lang.Thread.run(Thread.java:818) 
     Caused by: org.whispersystems.libsignal.InvalidMessageException: Bad Mac!
        at org.whispersystems.libsignal.protocol.SignalMessage.verifyMac(SignalMessage.java:119)
        at org.whispersystems.libsignal.SessionCipher.decrypt(SessionCipher.java:313)
        at org.whispersystems.libsignal.SessionCipher.decrypt(SessionCipher.java:268)
        at org.whispersystems.libsignal.SessionCipher.decrypt(SessionCipher.java:243) 
        at org.whispersystems.libsignal.SessionCipher.decrypt(SessionCipher.java:211) 
        at org.jivesoftware.smackx.omemo.signal.SignalOmemoRatchet.doubleRatchetDecrypt(SignalOmemoRatchet.java:122) 
        at org.jivesoftware.smackx.omemo.OmemoRatchet.retrieveMessageKeyAndAuthTag(OmemoRatchet.java:106) 
        at org.jivesoftware.smackx.omemo.OmemoService.decryptMessage(OmemoService.java:456) 
        at org.jivesoftware.smackx.omemo.OmemoService.onOmemoMessageStanzaReceived(OmemoService.java:1166) 
        at org.jivesoftware.smackx.omemo.OmemoManager$3$1.run(OmemoManager.java:945) 
        at java.lang.Thread.run(Thread.java:818) 
03-25 08:02:32.394 19291-3033/org.atalk.android W/aTalk: [8] org.jivesoftware.smackx.omemo.OmemoService.repairBrokenSessionWithPreKeyMessage() Attempt to repair the session by sending a fresh preKey message to swordfish@atalk.sytes.net:595475613

I doubt that. Usually libsignal will recognize the first message as a PreKeySignalMessage and create a session record with that message, so no NoRawSessionException will be thrown.

Yes, smack OMEMO captured the NoRawSessionException, and create the raw session. However after it created the session, it does not return to decrypt the message but just return decrypted which is null;

From the source below, the exception was thrown when it executes the statement

decrypted = decryptMessage(managerGuard, sender, element);

but it only logs the exception and return the decrypted (null). I am unable to trace where it actually re-decrypt the messages that causes the exception; also there is no message being displayed on aTalk chat session window on the recipient device for the very first OMEMO message. As this occur only once for new devices, hence no easily reproducible.

...
             decrypted = decryptMessage(managerGuard, sender, element);
            } catch (NoRawSessionException e) {
                OmemoDevice device = e.getDeviceWithoutSession();
                LOGGER.log(Level.WARNING, "No raw session found for contact " + device + ". ", e);

            } 
..
return decrypted;
  /**
     * Decrypt the OmemoElement inside the given Stanza and return it.
     * Return null if something goes wrong.
     *
     * @param stanza stanza
     * @param managerGuard authenticated OmemoManager
     * @return decrypted OmemoMessage or null
     * @throws IOException if an I/O error occurred.
     */
    OmemoMessage.Received decryptStanza(Stanza stanza, OmemoManager.LoggedInOmemoManager managerGuard) throws IOException {
        OmemoManager manager = managerGuard.get();
        // Avoid the ratchet being manipulated and the bundle being published multiple times simultaneously
        synchronized (manager) {
            OmemoDevice userDevice = manager.getOwnDevice();
            OmemoElement element = stanza.getExtension(OmemoElement.NAME_ENCRYPTED, OmemoElement_VAxolotl.NAMESPACE);
            if (element == null) {
                return null;
            }

            OmemoMessage.Received decrypted = null;
            BareJid sender;

            try {
                MultiUserChat muc = getMuc(manager.getConnection(), stanza.getFrom());
                if (muc != null) {
                    Occupant occupant = muc.getOccupant(stanza.getFrom().asEntityFullJidIfPossible());
                    Jid occupantJid = occupant.getJid();

                    if (occupantJid == null) {
                        LOGGER.log(Level.WARNING, "MUC message received, but there is no way to retrieve the senders Jid. " +
                                stanza.getFrom());
                        return null;
                    }

                    sender = occupantJid.asBareJid();

                    // try is for this
                    decrypted = decryptMessage(managerGuard, sender, element);

                } else {
                    sender = stanza.getFrom().asBareJid();

                    // and this
                    decrypted = decryptMessage(managerGuard, sender, element);
                }

                if (decrypted.isPreKeyMessage() && OmemoConfiguration.getCompleteSessionWithEmptyMessage()) {
                    LOGGER.log(Level.FINE, "Received a preKeyMessage from " + decrypted.getSenderDevice() + ".\n" +
                            "Complete the session by sending an empty response message.");
                    try {
                        sendRatchetUpdate(managerGuard, decrypted.getSenderDevice());
                    } catch (CannotEstablishOmemoSessionException e) {
                        throw new AssertionError("Since we successfully received a message, we MUST be able to " +
                                "establish a session. " + e);
                    } catch (NoSuchAlgorithmException | InterruptedException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
                        LOGGER.log(Level.WARNING, "Cannot send a ratchet update message.", e);
                    }
                }
            } catch (NoRawSessionException e) {
                OmemoDevice device = e.getDeviceWithoutSession();
                LOGGER.log(Level.WARNING, "No raw session found for contact " + device + ". ", e);

            } catch (CorruptedOmemoKeyException | CryptoFailedException e) {
                LOGGER.log(Level.WARNING, "Could not decrypt incoming message: ", e);
            }

            // Upload fresh bundle.
            if (getOmemoStoreBackend().loadOmemoPreKeys(userDevice).size() < OmemoConstants.PRE_KEY_COUNT_PER_BUNDLE) {
                LOGGER.log(Level.FINE, "We used up a preKey. Upload a fresh bundle.");
                try {
                    getOmemoStoreBackend().replenishKeys(userDevice);
                    OmemoBundleElement bundleElement = getOmemoStoreBackend().packOmemoBundle(userDevice);
                    publishBundle(manager.getConnection(), userDevice, bundleElement);
                } catch (CorruptedOmemoKeyException | InterruptedException | SmackException.NoResponseException
                                | SmackException.NotConnectedException | XMPPException.XMPPErrorException
                                | NotALeafNodeException e) {
                    LOGGER.log(Level.WARNING, "Could not republish replenished bundle.", e);
                }
            }
            return decrypted;
        }
    }

This topic was automatically closed 100 days after the last reply. New replies are no longer allowed.