One of the defining principles behind web technologies is building things once and making them available everywhere regardless of the device or platform. This inspired us to build a web version of Wire that anyone could use in a WebRTC-enabled browser.
Wire’s end-to-end encryption protocol Proteus, originally written in Rust, makes use of cryptographic primitives from the implementations of HMAC-SHA256, Curve25519 and HKDF. These algorithms are essential for everything that happens in Wire and are provided by the Sodium crypto library.
This speed difference between libsodium and libsodium.js forced us to look for alternatives that could increase the encryption and decryption speed in our web application. Fortunately we came across Neon, a Rust abstraction layer for native Node.js modules.
As Wire desktop apps are built with Electron (which runs Node.js), using Neon proved an ideal way to achieve near-native encryption and decryption performance.
The reason for using rustsodium instead of the well-known sodiumoxide is that we rely on libsodium’s system functions — which means every Wire user would need to have libsodium installed, or libsodium-neon would not work at all. However, rustsodium provides an option to download libsodium in advance and integrate it into the finished Node.js module. That’s the only functionality that sets it apart from sodiumoxide.
We then analyzed the cryptographic functions according to their performance in interpreted code (provided by libsodium.js), compiled code for Node.js (provided by libsodium-neon) and compiled code for native programs (provided by rust_sodium). The benchmark results were very positive:
Tested with Linux Debian 9, 64-bit (2.7 GHz Intel Core i7, 16 GB RAM)
As expected, rust_sodium is the fastest. It’s the big difference between libsodium-neon and libsodium.js that was a pleasant surprise. In almost all the functions the difference in speed is significant — up to 141 times faster.
The additional benefit is that we gain type safety as a result of the Rust bindings that are used by libsodium-neon.
Both of these advantages made Neon a very appealing choice. With a strict adherence to a well-defined interface, replacing existing calls to libsodium.js with libsodium-neon was as easy as this:
const sodium = require('libsodium-wrapper-sumo'); sodium.crypto_sign_verify_detached(signature, message, this.pub_edward);
const sodium = require('libsodium-neon'); sodium.crypto_sign_verify_detached(signature, message, this.pub_edward);
After implementing these changes, Wire on macOS and Linux is visibly faster on startup for example, when decrypting 1000 new messages:
Captured from the internal desktop build to show speed difference.
We’re happy to share that libsodium-neon has been already integrated into the latest versions of our macOS & Linux apps. Windows will get the update soon. (Update: Windows version containing this update was released in October 19, 2017.)
A big “Thank You!” to Dave Herman, the creator of Neon, who’s been collaborating with us on speeding up encryption in Wire.
The next step is to research a solution to bring similar speed improvements to the web version of Wire. You can keep an eye on our progress and check out our code on GitHub.
Benny Neugebauer, Florian Keller — Wire web/desktop team