Help error decrypting OX IM Message

Hi Paul,
I just followed the documentation. Which method exactly does that? AFAIK I’m supposed to only call announceSupportAndPublish(). How do I make sure that the PK are available on the receiving end? Is there a specific call?

Here you can see where the exception is thrown, so this might be a good place to start debugging.

I have checked that, it is this line that returns null. keyId have indeed a value.

I believe the keys are not being shared to the other end, but how do I do that? Am yet to find a method in documentation. I will appreciate pointing me to the right direction

The workflow is as follows:
Both devices generate a key pair and publish the public key to the server via pubsub.
This should all be done by announceSupportAndPublish().
As the keys are being distributed via pubsub, the devices should automatically receive the public keys of their contacts.
If you now send an OX encrypted message to a contact, smack will encrypt the message both to them using their public key and also using the public key of the sending device.

On the receiving side, the secret key that corresponds to the public key used for encryption must be available for the decryption to succeed.

I’d check if the key IDs of the used keys match. I.e. does the sender encrypt to the right keys or not?

You can also check out the OX integration tests for additional input :slight_smile:

Hi Paul,
thanks for your great comment.

Here is the problem. The key is not available (that is, returning null). I don’t know why it is that way. I believe am doing something wrong, but cannot find what it is. Am posting my relevant code in case you can spot what am doing wrong

You can also check out the OX integration tests for additional input

So far they have been so helpful in understanding a lot of things. Documentation on this is great too.

Am putting together a minimal code in few minutes and post here


class ChatManagerService : LifecycleService() {

    override fun onCreate() {
        super.onCreate()
        //...
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        super.onStartCommand(intent, flags, startId)

        //get username and password
        setUpConnection(username, password)
        // ...
        return START_STICKY
    }

    //...

    private fun setUpConnection(username: String?, password: String?) =
        lifecycleScope.launch(Dispatchers.IO) {
            val builder = XMPPTCPConnectionConfiguration.builder()
                .setUsernameAndPassword(username, password)
                .setHostAddress(InetAddress.getByName(Constants.JABBER_IP))
                .setXmppDomain(JidCreate.domainBareFrom(Constants.JABBER_DOMAIN))
                .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                .setSecurityMode(ConnectionConfiguration.SecurityMode.required)
                .setPort(5222)
                .setSendPresence(true)
                .setResource("ResourceMe")
                .enableDefaultDebugger()

            //...

            val conn = XMPPTCPConnection(builder.build())
            try {
                conn.connect()
                conn.login()

                //set MUC Manager and add listerners
                //...
                setupOpenPGP(conn)
                setupChats()

            } catch (e: Exception) {
                //...
            }
        }

    //...

    private fun setupOpenPGP(conn: XMPPTCPConnection) {
        //TODO: handle paraphrase with SecretKeyRingProtector
        val store = FileBasedOpenPgpStore(File(filesDir.absolutePath, "keys"))
        store.keyRingProtector = UnprotectedKeysProtector()

        val provider: OpenPgpProvider = PainlessOpenPgpProvider(connection, store)

        val openPgpManager: OpenPgpManager = OpenPgpManager.getInstanceFor(connection)

        val instantManager = OXInstantMessagingManager.getInstanceFor(conn)
        instantManager.addOxMessageListener { contact, _, decryptedPayload, _ ->
            val body = decryptedPayload.getExtension<Message.Body>(
                Message.Body.ELEMENT,
                Message.Body.NAMESPACE
            )
            Toast.makeText(this, body.message, Toast.LENGTH_LONG).show()
            Toast.makeText(this, contact.jid.toString(), Toast.LENGTH_LONG).show()
        }

        openPgpManager.openPgpProvider = provider

        openPgpManager.deleteSecretKeyServerBackup()

        //if (!openPgpManager.hasSecretKeysAvailable()) {
            openPgpManager.generateAndImportKeyPair(conn.user.asBareJid())
        //}

        openPgpManager.announceSupportAndPublish()
        //announce I support OpenGPG messages
        instantManager.announceSupportForOxInstantMessaging()
    }
}

Here is how I send ti

al domainAndAt = "@" + Constants.JABBER_DOMAIN
val to = JidCreate.entityBareFrom(toString + domainAndAt)

val builder = MessageBuilder.buildMessage(msgId).to(to)

val oxManager = OXInstantMessagingManager.getInstanceFor(connection)
val messageBody = Message.Body(null, Messagetext)
oxManager.addOxMessage(
    builder,
    OpenPgpManager.getInstanceFor(conn).getOpenPgpContact(to),
    listOf<ExtensionElement>(messageBody)
)

val message = builder.build()
DeliveryReceiptRequest.addTo(message)
ChatManager.getInstanceFor(conn)?.chatWith(to)?.send(message)

May be you or anyone reading this can spot what am doing wrong?

Found that I have a lot of Keys being sent and looking at the date, they are not being deleted. So may be one of those which is invalid is used to encrypt the message only to find it its respective PrivKey does not exist on other part. Am not sure if I have found the issue but I think it is great to update this thread for people who will find themselves in the same situation come tomorrow

<message to='123456@example.com/ResourceMe' from='7890@example.com' type='headline'>
      <event xmlns='http://jabber.org/protocol/pubsub#event'>
        <items node='urn:xmpp:openpgp:0:public-keys'>
          <item id='637D96655906E'>
            <public-keys-list xmlns='urn:xmpp:openpgp:0'>
              <pubkey-metadata v4-fingerprint='181BDA0928A97062123C91DA598692C09B732DC8' date='2020-06-10T16:04:42.359+00:00'/>
              <pubkey-metadata v4-fingerprint='1AEC2A9A1E10C7BDB47F1E9ECF10DA3E4BADB52F' date='2020-06-11T15:42:48.989+00:00'/>
              <pubkey-metadata v4-fingerprint='1B7C96B3684674553A30F877513D82740C55B882' date='2020-06-10T16:20:19.359+00:00'/>
              <pubkey-metadata v4-fingerprint='271B79BBF7035140DE00289536D20866D3039847' date='2020-06-10T13:49:59.795+00:00'/>
              <pubkey-metadata v4-fingerprint='2871A0AC15CED5C7339221AAE9F675A5D4446010' date='2020-06-10T16:12:43.123+00:00'/>
              <pubkey-metadata v4-fingerprint='2B3A46FFC94CC5D006C291B5A86FE9FFE810F295' date='2020-06-10T16:32:50.304+00:00'/>
              <pubkey-metadata v4-fingerprint='2BF784A282387F62D4713201899BADB53BA3381A' date='2020-06-10T16:19:35.084+00:00'/>
              <pubkey-metadata v4-fingerprint='4C08C6990D2F47A86052BDCCA831966CDF346EF2' date='2020-06-10T16:13:33.046+00:00'/>
              <pubkey-metadata v4-fingerprint='54FD709C86437B689B36DF0D462A80F4FE0D9B95' date='2020-06-10T16:27:14.836+00:00'/>
              <pubkey-metadata v4-fingerprint='630F05A1BC78E62ACEEDC8C5F451D657D33B5BBF' date='2020-06-10T12:55:20.835+00:00'/>
              <pubkey-metadata v4-fingerprint='718784BEFD9092B8BC92B4CE24D194EA7D3CEBE8' date='2020-06-10T13:25:52.523+00:00'/>
              <pubkey-metadata v4-fingerprint='900F95848F06B863C4D26B45D24FC76C9C69F619' date='2020-06-11T15:47:49.488+00:00'/>
              <pubkey-metadata v4-fingerprint='9ACA98632DC7C67EE74B2AB5203CDD64B5E4FD63' date='2020-06-09T20:43:39.020+00:00'/>
              <pubkey-metadata v4-fingerprint='A7C46818D173445CF9D07FC7066A65487857E658' date='2020-06-10T15:28:55.852+00:00'/>
              <pubkey-metadata v4-fingerprint='AF2F583BD739229F3521E9C4569285EAA5A4A767' date='2020-06-10T16:11:24.800+00:00'/>
              <pubkey-metadata v4-fingerprint='BD560C3580808A0036BE7C394565B53BE4513893' date='2020-06-10T14:16:28.242+00:00'/>
              <pubkey-metadata v4-fingerprint='DDB8FC3F827AFB17803380E14CC1B1678EE18839' date='2020-06-10T16:21:26.490+00:00'/>
              <pubkey-metadata v4-fingerprint='DF308A6F8B4F99B28F0185DA0699BC3F851FFF58' date='2020-06-09T20:18:38.592+00:00'/>
              <pubkey-metadata v4-fingerprint='EE35134C5434554D8209D64B9299134747F04E55' date='2020-06-10T15:11:54.924+00:00'/>
              <pubkey-metadata v4-fingerprint='F62FE63078D28D1117F6DCE4BE60A9BA0A0A5B23' date='2020-06-10T19:25:38.521+00:00'/>
            </public-keys-list>
          </item>
        </items>
      </event>
      <addresses xmlns='http://jabber.org/protocol/address'>
        <address jid='7890@example.com/ResourceMe' type='replyto'/>
      </addresses>
    </message>

Am trying to find an api to delete them all and cannot find yet. Does any exists?

There is no convenient method yet to delete published keys.

I’d say this is the reason why you have lots of keys published.
Basically at the moment your app generates and publishes a new key pair on every start.

You should try to get a clean environment by cleaning up old unused keys, as those will create issues such as this.

Unfortunately you have to do the cleaning manually using the PubSubManager api.

1 Like

I carelessly commented that and now I have a mess!

Unfortunately you have to do the cleaning manually using the PubSubManager api.

Never dealt with this API before but will have to figure out. Another option is just resetting my ejabberd test server

@Paul_Schaub I have fixed the keys issue but I still have not been successful. One more strange observation is the code below returns false despite announcing the support as shown above. Do you have idea why it is so?

OXInstantMessagingManager
    .getInstanceFor(connection)
    .contactSupportsOxInstantMessaging(to.asBareJid())

Did you also call OXInstantMessagingManager.announceSupportForOxInstantMessaging()?

Edit: Ah I see you called that method. That’s strange then… Maybe yet another server issue?
Afaict Smacks ServiceDiscovery implementation is pretty stable and tested, so I don’t suspect Smack to be the issue here…

I started with fresh ejabberd installation deleted everything and nothing works. I don’t know why it behaves that way. Can you test my code on your server to see if it is indeed my issue?

I’m out of ideas

so today got some free time and tried to figure out why oxManager.contactSupportsOxInstantMessaging(to.asEntityBareJid()) is always false. I chased it down the rabbit trail and found that this code in ServiceDiscoveryManager returns false. urn:xmpp:openpgp:im:0 is missing.

public boolean supportsFeatures(Jid jid, Collection<? extends CharSequence> features) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
        DiscoverInfo result = discoverInfo(jid);
        for (CharSequence feature : features) {
            if (!result.containsFeature(feature)) { //OX namespace is missing here
                return false; 
            }
        }
        return true;
    }

so here is the value of result. Note urn:xmpp:openpgp:im:0 is missing

<iq xmlns='jabber:client' to='xxxxxx@example.com/Resource' from='xxxxxxxx@example.com' id='1UTBG-12' type='result'>
    <query xmlns='http://jabber.org/protocol/disco#info'>
        <identity category='pubsub' type='pep' />
        <identity category='account' type='registered' />
        <feature var='http://jabber.org/protocol/disco#info' />
        <feature var='http://jabber.org/protocol/disco#items' />
        <feature var='vcard-temp' />
        <feature var='urn:xmpp:bookmarks-conversion:0' />
        <feature var='urn:xmpp:pep-vcard-conversion:0' />
        <feature var='msgoffline' />
        <feature var='http://jabber.org/protocol/offline' />
        <feature var='http://jabber.org/protocol/pubsub' />
        <feature var='http://jabber.org/protocol/pubsub#create-nodes' />
        <feature var='http://jabber.org/protocol/pubsub#auto-create' />
        <feature var='http://jabber.org/protocol/pubsub#auto-subscribe' />
        <feature var='http://jabber.org/protocol/pubsub#delete-nodes' />
        <feature var='http://jabber.org/protocol/pubsub#delete-items' />
        <feature var='http://jabber.org/protocol/pubsub#filtered-notifications' />
        <feature var='http://jabber.org/protocol/pubsub#modify-affiliations' />
        <feature var='http://jabber.org/protocol/pubsub#outcast-affiliation' />
        <feature var='http://jabber.org/protocol/pubsub#persistent-items' />
        <feature var='http://jabber.org/protocol/pubsub#publish' />
        <feature var='http://jabber.org/protocol/pubsub#publish-options' />
        <feature var='http://jabber.org/protocol/pubsub#purge-nodes' />
        <feature var='http://jabber.org/protocol/pubsub#retract-items' />
        <feature var='http://jabber.org/protocol/pubsub#retrieve-affiliations' />
        <feature var='http://jabber.org/protocol/pubsub#retrieve-items' />
        <feature var='http://jabber.org/protocol/pubsub#retrieve-subscriptions' />
        <feature var='http://jabber.org/protocol/pubsub#subscribe' />
        <feature var='http://jabber.org/protocol/commands' />
    </query>
</iq> 

I believe something is wrong with broadcasting support. But didn’t have enough time to figure out what is really wrong (required me reding all relevant XEPs and checking thru the code)

Will see if I can get another time for that but if this can help someone else dig deeper, that will be good!

Using a bare JID appears odd to me. I’d epxect you query a full JID here, as it’s clients who announce the feature, not the account on the server. Maybe @Paul_Schaub can clarify this.

1 Like

That makes sense. What (mis) lead me to that is method signature contactSupportsOxInstantMessaging(BareJid jid). Let us wait for @Paul_Schaub to clarify it

Hi @Flow,
was thinking and experimenting about this and it should not be full Jid since it will crash the app if the other end is offline at the moment of checking (with service not available). SO I think it registers the anouncement once and the other clients reads it there.

How does it do that, am yet to figure out!

Yeah that might be the issue.

I guess a fix would either be switching the argument to fulljid, or probably even better (considered we are talking about offline capable end-to-end encryption) create two methods. One named contactSupportsOX(BareJid) which returns true if there is a public key available and one deviceAnnouncedSupport(FullJid) which returns true if the specific resource announced support.

1 Like