The Trouble with crypto libraries

Cryptography is hard. Hard to design, hard to implement, hard to use, and hard to get right. 

Modern and well-studied ciphers rely on intractable problems and are believed to have a very high security margin, even in a post-quantum world.

But the cipher is rarely the weakest link in an application using cryptography. No matter how secure a function is, its security can be totally destroyed by a tiny weakness in its implementation or by using it incorrectly. And an application using cryptography has to do way more than picking a decently secure set of primitives.

The well-known OpenSSL library provides a wide range of cryptographic primitives and related helpers. Unfortunately, developers are likely to be confused by its overly-complex API. And sadly, the W3C crypto API is currently taking the very same path.

Is encryption all it takes to provide integrity? Is the key length the best indicator of how secure a cipher is? How should random numbers be generated? What are these “modes”? Is opting for the fastest one, like ECB, the best way to go? Why not just use SHA1(secret | message) instead of a HMAC?

Frequently, developers make arbitrary choices in response to the above questions, leading to security disasters. Worse, this happens most frequently when the intent was to achieve a very common operation.

In order to address this, Daniel J. Bernstein released NaCl.

NaCl is a new and very opiniated cryptographic library.

From a user perspective, it exposes a very simple, high-level API, with a tiny set of functions for each operation. Under the hood, it uses high-speed, highly-secure primitives and constructions, implemented with extreme care to avoid side-channel attacks. NaCl has no low-security options and makes very conservative choices of cryptographic primitives, while remaining exceptionally fast.

NaCl is an awesome toolkit for developers willing to add cryptography to their applications, that drastically reduces the risk of building insecure constructions. In fact, last year OpenDNS released DNSCrypt, an open-source tool I built for securing DNS communications, that leverages the NaCl crypto library.

But NaCl didn’t get much adoption, for several important reasons:

  • While each primitive comes with one or more portable implementations, NaCl itself is not portable. In particular, it can only be compiled on some Unix flavors.
  • NaCl ships with constructions that were just prototypes, and that shouldn’t be used any more.
  • The compiled code is only guaranteed to work on the machine it was compiled on.
  • NaCl is not a shared library, making it difficult to use in other programming languages, and it hasn’t been designed to be installed system-wide.
  • Packaging NaCl for different operating systems is tough due to its specific build system and compile-time requirements.

Well, NaCL just got a lot better. Today, I’m announcing Sodium, a portable, cross-compilable, installable, packageable, API-compatible version of NaCl. 

Sodium uses the same implementations of crypto primitives as NaCl, but is known to work on all the platforms supported by DNSCrypt, including Bitrig, OpenBSD, Dragonfly BSD, NetBSD, FreeBSD, SmartOS, OSX, Linux, Windows, iOS and Android.

Sodium installs a shared library and a standard set of headers, supports parallel compilation and testing, and uses a portable implementation of each primitive.

It also includes convenience functions for generating secure random numbers, and adds additional primitives and helpers to address common needs.

What Sodium does for you

  • Authenticated public-key and authenticated shared-key encryption
    This operation ensures that a message remains secret and can’t be modified by an attacker.
  • Public-key and shared-key signatures
    This operations allows the recipient of a message to verify that it actually comes from the expected sender, and that it hasn’t been modified.
  • Hashing
    This operation compresses a message to a short, fixed-size output from which the original message can’t be computed.
  • Keyed hashes for short messages
    A lot of applications and programming language implementations have been recently found to be vulnerable to denial-of-service attacks when a hash function with weak security guarantees, like Murmurhash 3, was used to construct a hash table.
    In order to address this, Sodium provides the “shorthash” function, currently implemented using SipHash-2-4. This very fast hash function outputs short, but unpredictable (without knowing the secret key) values suitable for picking a list in a hash table for a given key.
  • Secure pseudo-random numbers generation
    This generates pseudo-random numbers suitable for cryptographic purposes. Implementations can be dynamically chosen at run-time. On Windows platforms, it uses the Cryptographic Services Provider by default. Sodium also provides a secure, chroot()-resistant drop-in replacement for the arc4random() function family, including the ability to generate random numbers within a given interval with a nearly random distribution.

Why I built Sodium

One of the best things about working at OpenDNS is that the company is committed to the larger goal of making the Internet safer, and being transparent in our efforts. We build products that secure businesses against malware, but we also take time to build tools that can serve the greater needs of the security community. For example, earlier this year the Umbrella Security Labs announced the Umbrella Security Graph, a tool that helps researchers predict unknown threats. Before that, OpenDNS released DNSCrypt, an open-source tool I built for securing DNS communications, that leverages the NaCl crypto library. The company has been vocal on privacy, too (see OpenDNS’s open letter to congress on SOPA and anti-censorship policy). As an engineer and security advocate, I see cryptography as a critical building block to achieving privacy and security. I wanted to give back to the security community in a way that could make real impact. I hope you’ll find Sodium to be a valuable resource in your cryptography toolbox, and in your efforts to secure communications.

Installing Sodium

People quickly adopted Sodium and built packages for different operating systems.

The source code can also be downloaded here: Sodium source code or, for the bleeding-edge version, clone the Sodium Git repository

 

Using Sodium

While the Sodium library provides an API for the C language, bindings for other languages, currently Ruby and Python, are also available:

Python example

Installing the PyNaCl package

$ git clone git@github.com:dstufft/pynacl.git
$ cd pynacl
$ python setup.py install

Public-key signature

import nacl
key_seed = nacl.random(32)
private_key = nacl.signing.SigningKey(key_seed)

# this preprends a 512-bit signature to the message
signed_message = private_key.sign('message')

public_key = private_key.verify_key
# signing_key has to remain secret
# public_key should be sent to the recipient

Verifying the signature of a signed message

try:
message = public_key.verify(signed_message)
print(message)
except BadSignatureError:
sys.stderr.write("Incorrect signature\n")

Ruby example

Installing the RbNaCl gem

$ git clone git://github.com/cryptosphere/rbnacl.git
$ cd rbnacl
$ gem build rbnacl.gemspec
$ gem install rbnacl-*.gem

Public-key authenticated encryption

# Alice's key pair
alice_private_key = Crypto::PrivateKey.generate
alice_public_key = alice_private_key.public_key

# Bob's key pair
bob_private_key = Crypto::PrivateKey.generate
bob_public_key = bob_private_key.public_key

# Alice's "box" to encrypt/decrypt messages for/from Bob
alice_box = Crypto::Box.new(bob_public_key, alice_private_key)

# Bob's "box" to encrypt/decrypt messages for/from Alice
bob_box = Crypto::Box.new(alice_public_key, bob_private_key)

# Alice encrypts and signs a message for Bob.
# This operation needs a nonce for each message: a unique 192-bit value that
# should never ever be reused with the same key.
nonce = Crypto::Random.random_bytes(24)
ciphertext = alice_box.box(nonce, 'message')

# Bob verifies and decrypts Alice's message
begin
message = bob_box.open(nonce, ciphertext)
puts(message)
rescue Crypto::CryptoError
STDERR.puts("Ciphertext failed verification")
end

Privacy and security are important concerns, and cryptography is a critical building block to achieve these.