Sodium-Plus: A Positive Cryptography Experience for JavaScript Developers
Tags: cryptography, security, node, javascript
Published: 2019-10-07 11:20:51

---
title: Sodium-Plus: A Positive Cryptography Experience for JavaScript Developers
published: true
description: Writing secure software that uses the existing libsodium options is nontrivial and grueling. Sodium-Plus is a free and open source library that aims to provide a pleasant developer experience.
tags: cryptography, security, node, JavaScript
---

> Update (2019-10-11): I also published a [follow-up to this article](https://paragonie.com/blog/2019/10/improving-cryptography-javascript-ecosystem) that's a bit broader in scope.

If you ask around about implementing encryption or signatures in your apps, chances are someone will tell you to **just use libsodium**. And this is, truthfully, the correct answer for most people's problems.

However, the incumbent options for libsodium in the JavaScript ecosystem leave a *lot* to be desired. 

In particular, there are two back-end libraries that implement libsodium in JavaScript that I'll be discussing:

1. [sodium-native](https://github.com/sodium-friends/sodium-native), which is an unopinionated low-level binding of the C API
2. [libsodium-wrappers](https://github.com/jedisct1/libsodium.js) (and the other packages in the `libsodium.js` repository) which is cross-platform but slightly slower than sodium-native

### Encrypting a String in Sodium-Native

I bet you think you could just do this and call it a day?

```javascript
const sodium = require('sodium-native');

// Initialize with random bytes:
let key = sodium.randombytes_buf(32);
let nonce = sodium.randombytes_buf(24);
let message = "This is just an example string. Hello dev.to readers!";

// Encrypt:
let encrypted = sodium.crypto_secretbox_easy(message, nonce, key);

// Decrypt:
let decrypted = sodium.crypto_secretbox_open_easy(encrypted, nonce, key);
console.log(message === decrypted.toString());
```

Short, sweet, and to the point, right? Nope. That code doesn't work at all.


That snippet of code needs to be written like this:

```javascript
const sodium = require('sodium-native');

// Initialize with random bytes:
let key = sodium.randombytes_buf(32);
let nonce = sodium.randombytes_buf(24);
let message = Buffer.from("This is just an example string. Hello dev.to readers!");

// Encrypt:
let encrypted = Buffer.alloc(message.length + 16);
sodium.crypto_secretbox_easy(encrypted, message, nonce, key);

// Decrypt:
let decrypted = Buffer.alloc(encrypted.length - 16);
sodium.crypto_secretbox_open_easy(decrypted, encrypted, nonce, key);
console.log(message.toString() === decrypted.toString());
```

This API is *terrible* for JavaScript developers: Instead of returning a value, sodium-native overwrites one of the buffers you pass with the return value. Which means you have to allocate (and correctly size) buffers yourself.

Manual buffer allocation, especially to Node.js devs who learned before `Buffer.alloc()` and `Buffer.from()` became the norm, almost begs developers to write memory unsafe code. It also breaks if the user provides a string input instead of a `Buffer`.

### Encrypting a String in Libsodium.js

Fortunately, `libsodium-wrappers` does a fairly good job of exposing a usable in most cases. Except one caveat:

```javascript
const _sodium = require('libsodium-wrappers');
await _sodium.ready; // You can't use the library until it's ready
const sodium = _sodium;
```

Henceforth, the API consists entirely of synchronous functions.


```javascript
const _sodium = require('libsodium-wrappers');

(async function() {
  await _sodium.ready;
  const sodium = _sodium;

  // Initialize with random bytes:
  let key = sodium.randombytes_buf(32);
  let nonce = sodium.randombytes_buf(24);
  let message = "This is just an example string. Hello dev.to readers!";

  // Encrypt:
  let encrypted = sodium.crypto_secretbox_easy(message, nonce, key);

  // Decrypt:
  let decrypted = sodium.crypto_secretbox_open_easy(encrypted, nonce, key);
  console.log(message === decrypted.toString());
})();
```

### Other Differences and Design Warts

Compared with sodium-native, libsodium-wrappers is slightly slower (sodium-native calls a C library, where as libsodium-wrappers compiles the library with emscripten), but it runs in more places (i.e. in web browsers) and doesn't need a C compiler to get running.

Both libraries suffer from a subtle risk with X25519 keypairs: It's easy to accidentally get the public and secret key arguments mixed up and make your protocol insecure (although the unit tests will still pass).

Neither library works well with IDE code completion.

Neither library is particularly well-documented, either.

Because of these grievances, if a developer asked me today which of the two to use in a greenfield development project, I wouldn't be able to recommend either. Which is really sad because [the first two sentences of the official libsodium documentation](https://libsodium.gitbook.io/doc) state:

> Sodium is a modern, easy-to-use software library for encryption, decryption, signatures, password hashing and more.
>
> It is a portable, cross-compilable, installable, packageable fork of NaCl, with a compatible API, and an extended API to improve usability even further.

So, with that in mind, I'd like to introduce Sodium-Plus to the world.

# Introducing Sodium-Plus (Na+)

#### You can find [sodium-plus on Github](https://github.com/paragonie/sodium-plus) and [install it from NPM](https://www.npmjs.com/package/sodium-plus).

Sodium-Plus is the libsodium API that JavaScript developers deserve.

```javascript
const { SodiumPlus } = require('sodium-plus');

(async function() {
    // Select a backend automatically
    let sodium = await SodiumPlus.auto();

    let key = await sodium.crypto_secretbox_keygen();
    let nonce = await sodium.randombytes_buf(24);
    let message = 'This is just a test message';
    // Message can be a string, buffer, array, etc.

    let ciphertext = await sodium.crypto_secretbox(message, nonce, key);
    console.log(ciphertext);
    try {
        let decrypted = await sodium.crypto_secretbox_open(ciphertext, nonce, key);
        console.log(decrypted.toString('utf-8'));
    } catch (e) {
        console.error("Invalid ciphertext throws instead of returning false.");
    }
})();
```

**It's pluggable.** You can power it with either `sodium-native` if you're strictly a Node shop and need the performance, or `libsodium-wrappers` if you need cross-platform support. You can even install `sodium-native` on some builds and Sodium-Plus will automatically use it in the default configuration.

**It's asynchronous** where ever possible.

**It's fully type-safe.** You'll never accidentally get your public and secret keys mixed up with Sodium-Plus.

```javascript
const { SodiumPlus } = require('sodium-plus');

(async function() {
    // Select a backend automatically
    let sodium = await SodiumPlus.auto();
    console.log("Selected backend: " + sodium.getBackendName());

    let aliceKeypair = await sodium.crypto_box_keypair();
        let aliceSecret = await sodium.crypto_box_secretkey(aliceKeypair);
        let alicePublic = await sodium.crypto_box_publickey(aliceKeypair);

    // This works:
    let ciphertext = await sodium.crypto_box_seal(plaintext, alicePublic);
    let decrypted = await sodium.crypto_box_seal_open(ciphertext, alicePublic, aliceSecret);

    // These do not:
    try {
        ciphertext = await sodium.crypto_box_seal(plaintext, aliceSecret);
    } catch (e) {
        decrypted = await sodium.crypto_box_seal_open(ciphertext, aliceSecret, alicePublic);
        console.log(e); // TypeError { ... }
    }
})();
```

Feel free to run this code yourself, both with and without `sodium-native`.

-----

In virtually every way, we want Sodium-Plus to be an significant usability improvement over the existing libsodium implementations.

Additionally, we want to make sure it's easier to use Sodium-Plus than any other JavaScript cryptography libraries.

## What's the Project Status?

As of 2019-10-07:

* Version 0.1.0 (the first alpha) has been released, which only contains the most common features of libsodium.
* Many APIs (`generichash`, `secretstream`, etc.) have not yet been implemented in our library. The documentation has not been completed yet for what is implemented.
* However, *your IDE will autocomplete* correctly (due to our use of docblocks).

Our development roadmap is as follows:

1. Initial release. **We are here.**
2. Collect feedback from developers. (This is where I'd love your help!)
3. API completeness. (a.k.a. Finish wrapping the other libraries)
4. Complete the documentation.
5. Ensure 100% unit test coverage.
6. Ensure a `@types` bundle is available for TypeScript users.
7. Maybe get a security audit? (Not sure if the funding exists for this yet.)
8. Version 1.0.0. (Expected late 2019.)

## Where can I get Sodium-Plus?

* Github: https://github.com/paragonie/sodium-plus
* NPM: https://www.npmjs.com/package/sodium-plus