The 2018 XSF Summit

| categories: xmpp

This year I again attended the XMPP Software Foundation's summit in Brussels. The summit spans two days and is held at Cisco's offices.

It's a rare opportunity to get community members together to discuss and make decisions around protocol extensions (the so-called XEPs). On the second day interesting discussions were had regarding the state of XMPP today, for example how and by whom its used, how to foster and support client development and the merits of federation (i.e. communication between different messaging providers).

We heard that XMPP is heavily used and respected within the Telecoms industry, and that there's a lot of "hidden" use of XMPP that even most of the Summit participants were unaware of.

The distinction was made between XMPP, which represents the protocol, and Jabber®, which represents the ideal of federated instant messaging as used by end users.

Besides what some people may think, XMPP is alive and well, and used extensively in many applications and in various sectors.

Jabber®, on the other hand, not so much. Mindshare has moved largely to siloed (i.e. non-federated and non-standardized) messaging apps like Slack et al.

The Tigase guys Andrzej Wójcik and Daniel Wisnewski gave a demo of the new Internet-of-things module of the Tigase XMPP server which also drew attention at the realtime communications stand at FOSDEM.

Minutes for the XSF Summit

I volunteered to be the minute taker this year, for which I used my favorite notes-taking app: Vimwiki.

With Vimwiki you can generate HTML from your wiki files, so I was able to make the notes available online roughly as I was typing them. which I did by pushing the generated HTML to a git repo and then pulling again via cron on a server and serving with Nginx.

Me writing the notes at the summit.

I N C E P T I O N (an original piece by Guus der Kinderen)

I also wanted to make the minutes available on the XMPP wiki, but Vimwiki's syntax is a little bit different than the Mediawiki syntax.

Pandoc came to the rescue, and with a one-liner I could generate Mediawiki files from all my notes.

So here are the 2018 XSF Summit minutes on the XMPP wiki:

Final thoughts

It was fun to meet up again with kindred spirits and to see some familiar and friendly faces from the XMPP world.

This year was also the first time I attended the fancy XSF dinner. I had the sea bass with endive and beurre blanc.

See you folks next year and thanks for all the fish!

The XSF summit dinner.

Converse 3.3 has been released

| categories: xmpp, converse.js

Last night I released version 3.3.0 of Converse.js, and as often happens with big releases, I made a quick bugfix release (3.3.1) today.

The bugfix release turns off some CSS3 animations for new messages which caused degraded performance on Firefox. On Chrome the animations render smoothly, so if you'd like you can still turn them on with the show_message_load_animation config option.

What's in the release?

Maintaining a long-term open source front-end JavaScript library almost feels like a Sisyphean task sometimes. As soon as you've rolled the big stone up the hill, the whole JS ecosystem, best practices and tooling changes and you find yourself at the bottom of the hill again.

This release is therefore heavy on changes under the hood, with the aim of modernizing and improving the quality of the code.

Besides that, I also spent time squashing lots of small usability bugs and on improving performance.

Converse.js now uses a Virtual DOM

Various views, such as the registration form, are now rendered by means of a Virtual DOM. I wrote a new Backbone view called Backbone.VDOMView for this, and blogged about it here:

No more jQuery

Looking at the git log, I started rewriting code to not use jQuery in January 2017.

So this change has been a year in the works. I often asked myself whether I should spend time on this and not rather do something else, like adding new features, especially since removing jQuery has taken a lot of time.

However, there were some good reasons, or perhaps motivations, for me to remove jQuery.

Before ES6 promises were available, I used $.Deferred. However, jQuery's deferreds weren't compatible with Promises, so when ES6 Promises came around, I had to rewrite lots of code to use Promises.

I used $.show and $.hide quite a bit, and then it turned out that the way jQuery was doing it (by adding/removing display: none to the DOM element) is not good practice and also very slow.

So I started writing my own utility functions to replace jQuery's.

The last straw for me was when jQuery 3 came out, and half of Converse.js's ~240 tests failed once I plugged it in.

After spending some time trying to figure out what backward incompatible changes they made and how I should update the code, I decided to instead rip jQuery out entirely.

It's still used in the tests, but it's no longer included in any build.

Since removing it, I've noticed a remarkable reduction in time to run the tests.

By looking at how quickly the tests run now, the code seems to run much faster without jQuery.

Less weight

Besides removing jQuery, I also updated Converse.js to load translations at runtime, and only the exact translation JSON file that's needed.

Due to these changes, the unminified built has shrunk from 3.38MB to 2.8MB, and the minified build from 1.66MB to 1.2MB.

And this is while adding the virtual DOM code.

Route to a specific chat room via the URL

It's now possible to directly link to a specific chat room, or to the registration page (instead of the login page) via the URL.

For example, the URL will take you immediately to the Converse.js "Discuss" chat room, once you've logged in.

What else?

Lots of other bugfixes and improvements have been added in this release. For more details, check the changelog.

Notable absent from this release are some desired features, such as file sharing, message corrections, message receipts and the like.

I would love to add some of these often requested features, however I had to get the house in order so to speak, by modernizing the code and squashing lots and lots of little usability and performance bugs.

That said, Converse.js takes up a LOT of my free time and not a single line of code in this release was paid for.

If you or your company make use of converse.js, please consider sponsoring it on Patreon or Liberapay.


Thanks goes out to everyone who's made pull requests and bug reports over the last months.

And thanks also to the folks who hang out in the Converse.js Discusss chat room and who have there provided valuable feedback.

Customizing and whitelisting SASL authentication mechanisms in Strophe.js

| categories: xmpp, strophe.js, foss, sasl


If you've decided to read this fairly technical blogpost, then you probably have at least a rough idea what SASL is about and why one would want to create custom SASL auth mechanisms or whitelist the supported mechanisms.

I'll therefore provide just a very brief recap of the topics involved:

The Simple Authentication and Security Layer or SASL RFC 4422 is a framework for adding authentication support to connection-based protocols.

It provides an abstraction layer for authentication mechanisms, so that protocols, such as XMPP don't have to deal with the intricacies and complexities of supporting multiple authentication mechanisms.

It therefore makes auth mechanisms pluggable (if they are SASL compatible).

Strophe.js has supported SASL since a long time, but it didn't provide an easy way add custom SASL mechanisms, or to whitelist the mechanisms to be used.

Until now... or rather, since the 1.2.9 release.

Creating a custom SASL auth mechanism

To create a custom SASL authentication mechanism is fairly simple.

You can glean what's required by simply looking at how the default mechanisms are created.

See for example how the SASLPlain mechanism is defined.

And look as the SASLMechanism prototype to see the interface that the mechanism supports.

Perty much it boils down to creating constructor, settings its prototype to an invoked Strophe.SASLMechanism instance, providing its name, a boolean to indicate whether it should proactively respond without an initial server challenge, and an integer value specifying its priority amongst the supported mechanisms.

The default mechanisms and their respective priorities are:

  • EXTERNAL - 60
  • SCRAM-SHA1 - 40
  • DIGEST-MD5 - 30
  • PLAIN - 20
  • ANONYMOUS - 10

Then it's a matter of implementing onChallenge and any of the other methods provided by the SASLMechanism prototype.

onChallenge is called once the server challenges the client to authenticate itself or proactively if the mechanism requires that the client initiates authentication (configured with the isClientFirst parameter of Strophe.SASLMechanism).

So, lets create a fictional auth mechanism called SASL-FOO which works similarly to SASL-PLAIN, except that the password is encrypted with double-encoding ROT13 (hint: this is a joke).

We would then create the authentication mechanism like so:

Strophe.SASLFoo = function() {};
Strophe.SASLFoo.prototype = new Strophe.SASLMechanism("FOO", true, 60);

Strophe.SASLFoo.prototype.onChallenge = function(connection) {
    var auth_str = connection.authzid;
    auth_str = auth_str + "\u0000";
    auth_str = auth_str + connection.authcid;
    auth_str = auth_str + "\u0000";
    auth_str = auth_str + DoubleROT13(connection.pass);
    return utils.utf16to8(auth_str);

Whitelisting the supported SASL auth mechanisms

Now with SASL-FOO in hand, we can whitelist the supported authentication mechanisms by specifying a list of mechanisms in the options map passed in when we instantiate a new Strohe.Connection.

var service = '';
var options = {
    'mechanisms': [
var conn = new Strophe.Connection(service, options);

Bonus: Whitelisting SASL auth mechanisms in Converse.js

Due to the above changes it'll also be possible to whitelist SASL mechanisms in Converse.js (version 2.0.1 and upwards).

This is done via the connection_options configuration setting:


    connection_options: {
        'mechanisms': [

Strophe.js and Converse now support passwordless login with client certificates

| categories: xmpp, converse, sasl, strophe.js, foss, openfire


Did you know that x509 certificates, the certificates that webservers use to prove their identity during the establishment of an HTTPS connection, can also be used by a client (like your webbrowser) to prove its identity, and even to authenticate?

I'm talking here about so-called client certificate authentication.

Client certificate authentication is especially popular in environments with high security requirements. They can even be used to enforce 2-factor authentication, if in addition to a client certificate you also require a password. That usecase is however out of scope for this blog post.

With the release of Strophe.js 1.2.8, it's now possible to have passwordless login with TLS client certificates in Converse and any other Strophe.js-based webchat projects.

For Converse, you'll need at least version 2.0.0.

Here's what it looke like:

Logging in with an SSL client certificate

The technical details and background


The XMPP logo

XMPP supports authentication with client certificates, because it uses SASL (Simple Authentication and Security Layer).

SASL provides an abstraction that decouples authentication mechanisms from application protocols.

This means that XMPP developers don't need to know about the implementation details of any authentication mechanisms, as long as they conform to SASL.

Up til version 1.2.7, Strophe.js supported the SASL auth mechanisms: ANONYMOUS, OAUTHBEARER, SCRAM-SHA1, DIGEST-MD5 and PLAIN.

For client certificate auth, we need another SASL mechanism, namely EXTERNAL. What EXTERNAL means, is that authentication happens externally, outside of the protocol layer. And this is exactly what happens in the case of client certificates, where authentication happens not in the XMPP layer, but in the SSL/TLS layer.

Strophe.js version 1.2.8 now supports SASL-EXTERNAL, which is why client certificate authentication now also works.

How do you communicate with an XMPP server from a web-browser?

There are two ways that you can communicate with an XMPP server from a web-browser (e.g. from a webchat client such as Converse).

  1. You can use XMLHttpRequests and BOSH, which you can think of as an XMPP-over-HTTP specification.
  2. You can use websockets.

Both of these protocols, HTTP and websocket, have secure SSL-reliant versions (HTTPS and WSS), and therefore in both cases client certificate authentication should be possible, as long as the server requests a certificate from the client.

I'm going to focus on BOSH and HTTPS, since this was my usecase.

The HTTPS protocol makes provision for the case where the server might request a certificate from the client.


NOTE: Currently the only XMPP server that supports client certificate authentication with BOSH is Openfire, and funnily enough, only Openfire 3. In Openfire 4, they refactored the certificate handling code and broke client certificate authentication with BOSH. I've submitted a ticket for this to their tracker:

The authentication flow

So this is how the authentication flow works. I'll illustrate how the authentication flow works by using actual log output from Converse


NOTE: My XMPP server's domain is called debian, because I was running it on a Debian server and because naming things is hard. In hindsight, this wasn't a good name since it might confuse the dear reader (that means you).

2016-09-15 12:07:05.481 converse-core.js:128 Status changed to: CONNECTING

Firstly, Converse sends out a BOSH stanza to the XMPP server debian, to establish a new BOSH session.

2016-09-15 12:07:05.482 converse-core.js:128
    <body rid="1421604076"
          to="debian" xml:lang="en" wait="60"
          hold="1" content="text/xml; charset=utf-8"
          ver="1.6" xmpp:version="1.0"
2016-09-15 12:07:06.040 bosh.js:749 XHR finished loading: POST "https://debian:7445/http-bind/"

The above stanza was sent as an XMLHttpRequest POST, and the above XML was sent as the Request Payload.

Strophe.js takes care of all this, so nothing to worry about, but sometimes digging through the internals is fun right? Right?!

2016-09-15 12:07:06.042 converse-core.js:128
    <body xmlns=""
          xmlns:stream="" from="debian"
          authid="fe0ee6ab" sid="fe0ee6ab" secure="true" requests="2"
          inactivity="30" polling="5" wait="60"
          hold="1" ack="1421604076" maxpause="300" ver="1.6">
            <mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
            <register xmlns=""/>
            <bind xmlns="urn:ietf:params:xml:ns:xmpp-bind"/>
            <session xmlns="urn:ietf:params:xml:ns:xmpp-session">

So now the XMPP server, debian, has responded, and it provides a list of SASL mechanisms that it supports. In this case it only supports EXTERNAL.

Luckily our webchat client supports SASL-EXTERNAL, so it responds in turn and asks to be authenticated.

2016-09-15 12:07:06.147 converse-core.js:128
    <body rid="1421604077" xmlns=""
        <auth xmlns="urn:ietf:params:xml:ns:xmpp-sasl"

Now here comes the tricky part. The XMPP server's BOSH servlet, asks the webbrowser (which is establishing the HTTPS connection on our behalf) to give it the client certificate for this user.

The webbrowser will now prompt the user to choose the right client certificate. Once this is done, the XMPP server authenticates the user based upon this certificate.

2016-09-15 12:07:06.177 bosh.js:749 XHR finished loading: POST

2016-09-15 12:07:06.180 converse-core.js:128
    <body xmlns="" ack="1421604077">
        <success xmlns="urn:ietf:params:xml:ns:xmpp-sasl"/>

The XMPP server responds with success and we're logged in!

How to set up client certificate authentication with Converse and OpenFire 3.10.3


NOTE: Thanks goes out to Dennis Shtemberg from Infusion, who initially tested client certificate authentication with BOSH on Openfire and on whose notes the following is based.

1. Install Openfire 3.10.3

The XMPP logo

On Debian(-based) Linux, you can simply do the following:

sudo dpkg -i openfire_3.10.3_all.deb

2. Configure Openfire's system properties

Open the admin console: http://localhost:9090/ (where localhost is the host the server is running on)

Navigate to Server > Server Manager > System Properties and add the following properties:

Property Value
xmpp.client.cert.policy needed
xmpp.client.certificate.accept-selfsigned true
xmpp.client.certificate.verify true
xmpp.client.certificate.verify.chain true
xmpp.client.certificate.verify.root true
sasl.mechs EXTERNAL

Make sure the xmpp.domain value is set to the correct host. If you're running Openfire on localhost, then you need to set it to localhost. If you're not using localhost, then replace all mention of localhost below with the xmpp.domain value.

3. Lay the groundwork for generating an SSL client certificate

First, make sure you have OpenSSL installed: aptitude install openssl Then create a directory for certificate files: mkdir ~/certs

Now create a config file called user01.cnf (~/certs/user01.cnf) with the following contents:

x509_extensions = v3_extensions
req_extensions = v3_extensions
distinguished_name = distinguished_name

extendedKeyUsage = clientAuth
keyUsage = digitalSignature,keyEncipherment
basicConstraints = CA:FALSE
subjectAltName = @subject_alternative_name

otherName.0 =;UTF8:user01@localhost

commonName = user01@localhost

The otherName.0 value under subject_alternative_name assigns the user's JID to an ASN.1 Object Identifier of "id-on-xmppAddr". The XMPP server will check this value to figure out what the JID is of the user who is trying to authenticate.

For more info on the id-on-xmppAddr attribute, read XEP-178.

4. Generate an SSL client certificate

  • Generate a self-signed, leaf SSL certificate, which will be used for client authentication.

    • Generate a private RSA key

      openssl genrsa -out user01.key 4096

    • Generate a sigining request:

      openssl req -key user01.key -new -out user01.req -config user01.cnf -extensions v3_extensions

      • when prompted for a DN enter: user01@localhost
    • Generate a certificate by signing user01.req

      openssl x509 -req -days 365 -in user01.req -signkey user01.key -out user01.crt -extfile user01.cnf -extensions v3_extensions

    • Generate PKCS12 formatted certificate file, containing the private key and the certificate. This will be the client certificate which you will log in with.

      openssl pkcs12 -export -inkey user01.key -in user01.crt -out user01.pfx -name user01

      • when prompted for export password enter: user01

5. Install the PKCS12 certificate on your local machine

Double click the pfx file and follow the steps to import it into your machine's keystore.

6. Import the x509 certificate into Openfire

sudo keytool -importcert -keystore /etc/openfire/security/truststore -alias user01 -file ~/certs/user01.crt sudo keytool -importcert -keystore /etc/openfire/security/client.truststore -alias user01 -file ~/certs/user01.crt sudo systemctl restart openfire


NOTE: The default keystore password is "changeit"

7. Create the user associated with the SSL client certificate

Go back to Openfire admin console, navigate to Users/Groups > Create New User and create a new user.

  • Username: user01
  • Password: user01 (This is not controlled by Openfire).
  • Click Create User

8. (When using Java 1.7) Patch Openfire

When trying to log in, I received the following error:

2016.09.08 00:28:20 org.jivesoftware.util.CertificateManager - Unkown exception while validating certificate chain: Index: 0, Size: 0

Turns out the likely cause for this is the fact that I was using the outdated Java version 1.7.

At the time, I didn't know that Java is the culprit, so I patched the following code

If you read the comments in the link above, you'll see there are two sections, with one being outcommented. I swopped out the two sections, and then recompiled Openfire.

After that, client certificate auth worked. The best way to avoid doing this is apparently to just use Java 1.8.

9. Test login with Converse

The Converse logo

Now you're done with setting up Openfire and you can test logging in with Converse.

Download the latest version of Converse from the releases page.

To hide the password field (since the password won't be checked for anyway), you need to open index.html in your text editor and add authentication: 'external to the converse.initialize call.

Then open index.html in your browser.

In the Converse login box, type the JID of the user, e.g. user01@localhost and click login.


NOTE: If things go wrong, pass debug: true to converse.initialize, then open your browser's developer console and check the output. Check especially the XHR calls to http-bind. Checking the output in the Network tab can also be very helpful. There you'll see what Openfire responds to requests to its BOSH URL.


Client certificate authentication is a bit of a niche requirement, doing so with BOSH/HTTP even more so.

However, I expect webchat XMPP clients to become more and more prevalent in the coming years, even on the desktop, for example when packaged with Github's Electron (an Electron version of Converse is planned BTW, based on the fullscreen version inverse.js).

The fact that this works because of SASL-EXTERNAL authentication being added to Strophe.js means that this functionality is not only possible in Converse, but all webchat clients built on Strophe.js (granted that they use version 1.2.8 or higher).

Unfortunately XMPP server support is lacking, with only Openfire supporting this usecase currently, and not yet (at the time of writing) in the 4.0.x branch. To see whether this gets fixed, keep an eye on the relevant ticket

Converse.js 0.7 released

| categories: javascript, cryptography, xmpp, converse, chat, otr, foss

Now supports Off-the-record encryption

I'm happy to announce that the 0.7 release of Converse.js now supports Off-the-record (OTR) encrypted messaging.

The OTR protocol not only encrypts your messages, it provides ways to verify the identity of the person you are talking to, plausible deniability and perfect forward secrecy by generating new encryption keys for each conversation.

How this works is actually fascinating. The encryption keys are generated via a Diffie-Helman exchange. On youtube there is a video with a very simple and intuitive explanation on how the Diffie-Helman key exchange works. For more details, you can also read the OTR version 3 protocol spec.

I was able to add OTR support for converse.js by using Arlo Breault's excellent otr.js library, which is also used in Cryptocat and Diaspora.

Many thanks go out to him for this.



Arlo cautions that the otr.js library has not yet been properly vetted by security researchers and shouldn't be used in life or death situations.

This word of caution applies doubly so to its integration into converse.js!

I'm not a professional cryptographer and this should be seen as an experimental feature.

Caveat emptor!


Note: For detailed fullscreen, make sure that video quality is set to HD, by clicking the gear icon on the video player.

On Javascript Cryptography

Implementing Cryptography in Javascript is relatively controversial and in 2011 Matasano security wrote a scathing criticism called Javascript Cryptography Considered Harmful.

I read that article before I started working on OTR support for converse.js and I'd like to go through some of its criticisms step by step to explain how they might apply to converse.js and what I've done to mitigate them.

If you're going to read on, it would make sense to go read that article first.

Secure delivery of Javascript to browsers being a chicken-egg problem

Their criticism appears to apply to the case where developers use Javascript cryptography to implement secure communications with a server instead of relying on a standard protocol such as SSL/TLS.

The OTR crypto is not trying to compete with or replace SSL/TLS.

Sending data in the clear with normal HTTP is not secure and should be avoided. Therefore, if you are going to use OTR with converse.js, always make sure that the site users HTTPS (i.e. SSL/TLS).

Ideally you would also want a mechanism to sign the Javascript and then verify it's authenticity once it's been downloaded.

This can be done with code that runs in browser extensions (like Cryptocat does), but (AFAIK) unfortunately not with browser-agnostic javascript (like converse.js).

This looks to me like the achilles heel for browser javascript crypto.

Besides the webserver serving the Javascript, it's also important that the BOSH service you use employs SSL/TLS, as well as the XMPP server the BOSH service connects to.

The XMPP community is on a drive to secure all the major public XMPP servers. See the Manifesto created by Peter Saint-Andre and signed by many in the XMPP community.

On they let you test the security of servers on the Jabber/XMPP network.

Besides using javascript served via TLS, you can also download the converse.js tarball to your computer, unpack the files and access them directly. The index.html page will open up a page identical to the one on

Browser Javascript being hostile to cryptography

The prevalence of content-controlled code.

If the website fetches javascript from thirdparty websites (for example to serve advertisements), then you not only have to trust the server to serve non-malicious code, but also all those 3rd party servers.

The solution is to not use such a site for secure communications. Clear your cache regularly, Use hosts you trust, or as I've mentioned above, bypass the webserver entirely by using converse.js from your own filesystem.

The malleability of the Javascript runtime.

Javascript is indeed very malleable. Methods and attributes on objects can be replaced during run-time (see Duck Typing and Monkey Patching),

This makes it easy to inject malicious code into Javascript.

However, in order to do this, the attacker's code needs access to those Javascript objects, and fortunately Javascript provides a way of hiding sensitive objects inside closures, thereby preventing an attacker from accessing and manipulating them.

Simply put, a closure defines a limited scope for objects, variables and functions which when used correctly, cannot be accessed from any other Javascript code.

Any variables, objects or functions defined locally inside another (parent) function (with the var) keyword, are not accessible outside of that parent function.

If the parent functions is anonoymous (is not referenced anywhere) or the child function's runtime outlives the parent's, you have a private, enclosed scope with data that cannot be accessed from outside that scope.

All the data in converse.js is encapsulated inside such closure.

The lack of systems programming primitives needed to implement crypto.

Matasano claims that Javascript lacks a secure random number generator RNG.

Without having authoritative knowledge on whether their claim is valid, I'll refer you to this blog post by Nadim Kobeissi (author of Cryptocat), where he claims:

This is in fact not the case. window.crypto.getRandomValues() is a secure random number generator available in JavaScript, and supported by major web browsers.

His blog post is well worth reading, and he also addresses some other criticisms for which I didn't have anything to add.


OTR encryption in converse.js provides increased security and privacy for your chats. Messages are encrypted and not logged or cached

Any Jabber/XMPP services that snoop on your communications by logging your messages (like for example Facebook does) will only have access to the encrypted ciphertext, making it useless for surveillance or exploitation.

Features coming future releases

The current implementation of OTR doesn't have any encryption policy support. With that, I mean the ability to set a policy such as Always encrypt messages.

Currently you need to always manually enable encryption.

Distributed services and self-hosting

There are more and more Free and Open Source (FOSS) self-hosted web-applications becoming available as alternatives to so-called centralized "cloud" solutions.

See for example roundcube, kolab, ownCloud, MailPile, friendica and buddycloud.

Converse.js can be integrated into any of these applications, and since you should ideally host them yourself, you'll have more control on what Javascript is actually being served.

Feedback, patches, donations

All of the work I did on OTR integration was in my free time and free of charge. A gift and my personal contribution towards the movement for providing free communication solutions that are not founded upon surveillance and commercial exploitation of personal data.

All the code is open source and contributions are always welcome, be it in audits, features, bugfixes, documentation, words of encouragement or tips.

If you're so inclined, I'd appreciate any tips at the following bitcoin address: 16FsPqE9DhFTryxrUenpsGX4LJ1TPu8GqS

« Previous Page -- Next Page »